def test_layout_clusters_get_max_logical_extent(): surface = SVGSurface(None, 100, 100) cairo_context = Context(surface) layout_1 = pangocairocffi.create_layout(cairo_context) layout_1.set_markup('Text with one line') layout_1_clusters = LayoutClusters(layout_1) extent_1 = layout_1_clusters.get_max_logical_extent() layout_2 = pangocairocffi.create_layout(cairo_context) layout_2.set_markup('text with\ntwo lines') layout_2_clusters = LayoutClusters(layout_2) extent_2 = layout_2_clusters.get_max_logical_extent() assert extent_1.x == 0 assert extent_1.y == 0 assert extent_1.width > 0 assert extent_1.height > 0 assert extent_2.x == extent_1.x assert extent_2.y == extent_1.y assert extent_1.width != extent_2.width assert extent_1.height * 2 == extent_2.height surface.finish()
def test_pdf(): filename = 'tests/output/glyph_item.pdf' pt_per_mm = 72 / 25.4 width, height = 210 * pt_per_mm, 297 * pt_per_mm # A4 portrait surface = cairocffi.PDFSurface(filename, width, height) ctx = cairocffi.Context(surface) ctx.translate(width / 4, 100) layout = pangocairocffi.create_layout(ctx) layout.set_width(pangocffi.units_from_double(width / 2)) layout.set_alignment(pangocffi.Alignment.CENTER) layout.set_markup( '<span font="italic 30">Hi from Παν語</span>\n' '<span font="sans-serif">The text layout engine library for ' 'displaying <span font-weight="bold">multi-language</span> text!\n' 'Arabic: السَّلام عليكُم\n' 'Hebrew: שלום\n' 'Hindi नमस्ते, नमस्कार।\n' 'Russian: Здравствуйте!\n' 'Japanese: こんにちは, コンニチハ</span>' ) y_diff = 200 pangocairocffi.show_layout(ctx, layout) ctx.translate(0, y_diff) render_run_glyph_items(ctx, layout) ctx.translate(0, y_diff) render_cluster_glyph_items(ctx, layout) surface.finish()
def build_layout( ctx: cairo.Context, pctx: pangocffi.Context, parsed_text, style: TextStyle, styles, resolution_scale: float, spacing_scale: float = 1.0, text_scale: float = 1.0, ) -> pangocffi.Layout: # TODO: fix letter spacing style = styles["default"].compose(style) layout = pangocairocffi.create_layout(ctx) layout.set_ellipsize(pangocffi.EllipsizeMode.NONE) layout.set_alignment(get_pango_alignment(style)) text, attributes = get_text_and_attributes(parsed_text, style, styles, text_scale) layout.set_text(text) layout.set_attributes(attributes) height = get_text_height(pctx, style, resolution_scale) assert style.line_spacing >= 1.0 spacing = (style.line_spacing * style.size) - height layout.set_spacing(to_pango_units(spacing * spacing_scale)) return layout
def test_vertical_offset_works(self): surface, cairo_context = self._create_real_surface( 'vertical_offset_works.svg' ) layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[5, 40], [45, 40], [90, 80]]) for vertical_offset in [-13, 0, 13]: offset_linestring, _ = parallel_offset_with_matching_direction( line_string, vertical_offset, side=Side.LEFT ) debug.draw_line_string(cairo_context, offset_linestring) red = max(0.0, vertical_offset / 13) green = max(0.0, -vertical_offset / 13) cairo_context.set_source_rgba(red, green, 0, 0.2) cairo_context.stroke() text_path = TextPath(line_string, layout) text_path.vertical_offset = vertical_offset cairo_context.set_source_rgb(red, green, 0) text_path.draw(cairo_context) surface.finish()
def test_pdf(): filename = 'tests/output/error_underline.pdf' pt_per_mm = 72 / 25.4 width, height = 45 * pt_per_mm, 10 * pt_per_mm # A4 portrait surface = cairocffi.PDFSurface(filename, width, height) ctx = cairocffi.Context(surface) layout = pangocairocffi.create_layout(ctx) layout.set_width(pangocffi.units_from_double(width)) layout.set_alignment(pangocffi.Alignment.CENTER) layout.set_markup('Hi from Παν語') layout_logical_extent = layout.get_extents()[1] layout_baseline = pangocffi.units_to_double(layout.get_baseline()) layout_logical_extent_pixels = _convert_rectangle_to_pixels( layout_logical_extent) pangocairocffi.show_layout(ctx, layout) ctx.set_source_rgb(0.8, 0, 0) pangocairocffi.show_error_underline(ctx, layout_logical_extent_pixels[0], layout_baseline, layout_logical_extent_pixels[2], 2) surface.finish()
def test_compute_boundaries(self): surface, cairo_context = self._create_real_surface( 'upright_text_path_compute_boundaries.svg') layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[0, 0], [100, 0]]) text_path = UprightTextPath(line_string, layout) assert text_path.compute_boundaries() is None
def test_error_is_raised_for_multi_line(self): surface, cairo_context = self._create_void_surface() layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語\nThis is a text') line_string = LineString([[0, 0], [100, 0]]) with self.assertRaises(ValueError): TextPath(line_string, layout)
def write_text(ctx, text, start_x, start_y): ctx.move_to(start_x, start_y) layout = pangocairocffi.create_layout(ctx) layout.set_width(pangocffi.units_from_double(width)) #layout.set_alignment(pangocffi.Alignment.CENTER) fontdesc = pangocffi.FontDescription() fontdesc.set_size(pangocffi.units_from_double(3)) fontdesc.set_family(font_family) layout.set_font_description(fontdesc) layout.set_text(text) pangocairocffi.show_layout(ctx, layout)
def test_vertical_offset_fails_on_invalid_offset_shape(self): surface, cairo_context = self._create_void_surface() layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[0, 0], [40, 0], [40, 40], [0, 40], [0, 0]]) text_path = TextPath(line_string, layout) text_path.vertical_offset = -10 with self.assertRaises(RuntimeError): text_path.draw(cairo_context)
def test_text_fits(self): surface, cairo_context = self._create_real_surface('text_fits.svg') layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[0, 0], [600, 0]]) text_path = TextPath(line_string, layout) assert text_path.text_fits() line_string = LineString([[0, 0], [50, 0]]) text_path = TextPath(line_string, layout) assert not text_path.text_fits()
def _show_label( context: cairocffi.Context, position: Tuple[float, float], width: float, text: str ): context.translate(position[0], position[1]) label = pangocairocffi.create_layout(context) label.set_width(pangocffi.units_from_double(width)) label.set_alignment(pangocffi.Alignment.LEFT) label.set_markup('<span font-family="sans-serif">%s</span>' % text) pangocairocffi.show_layout(context, label) context.translate(-position[0], -position[1])
def text2svg(self): """Internally used function. Convert the text to SVG using Pango """ size = self.size * 10 line_spacing = self.line_spacing * 10 dir_name = config.get_dir("text_dir") disable_liga = self.disable_ligatures if not os.path.exists(dir_name): os.makedirs(dir_name) hash_name = self.text2hash() file_name = os.path.join(dir_name, hash_name) + ".svg" if os.path.exists(file_name): return file_name surface = cairocffi.SVGSurface(file_name, 600, 400) context = cairocffi.Context(surface) context.move_to(START_X, START_Y) settings = self.text2settings() offset_x = 0 last_line_num = 0 layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(600)) for setting in settings: family = setting.font style = self.str2style(setting.slant) weight = self.str2weight(setting.weight) text = self.text[setting.start : setting.end].replace("\n", " ") fontdesc = pangocffi.FontDescription() fontdesc.set_size(pangocffi.units_from_double(size)) if family: fontdesc.set_family(family) fontdesc.set_style(style) fontdesc.set_weight(weight) layout.set_font_description(fontdesc) if setting.line_num != last_line_num: offset_x = 0 last_line_num = setting.line_num context.move_to( START_X + offset_x, START_Y + line_spacing * setting.line_num ) pangocairocffi.update_layout(context, layout) if disable_liga: text = escape(text) layout.set_markup(f"<span font_features='liga=0'>{text}</span>") else: layout.set_text(text) logger.debug(f"Setting Text {text}") pangocairocffi.show_layout(context, layout) offset_x += pangocffi.units_to_double(layout.get_size()[0]) surface.finish() return file_name
def test_layout_clusters_properties_have_same_length(): surface = SVGSurface(None, 100, 100) cairo_context = Context(surface) layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語\nThis is a test') expected_length = len(layout.get_text()) - 1 # Subtract newline char layout_clusters = LayoutClusters(layout) assert layout_clusters.get_layout() is layout assert len(layout_clusters.get_clusters()) == expected_length assert len(layout_clusters.get_logical_extents()) == expected_length surface.finish()
def test_t2s() -> None: size = 1 temp_pango_text = Text("Helloworld", t2s={"world": ITALIC}) surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH) context = cairocffi.Context(surface) context.move_to(START_X, START_Y) layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(WIDTH)) fontdesc = pangocffi.FontDescription() fontdesc.set_size(pangocffi.units_from_double(size * 10)) layout.set_font_description(fontdesc) layout.set_markup( 'Hello<span style="italic">world</span>') # yay, pango markup pangocairocffi.show_layout(context, layout) surface.finish() assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
def test_pdf(): filename = 'tests/output/test.pdf' pt_per_mm = 72 / 25.4 width, height = 210 * pt_per_mm, 297 * pt_per_mm # A4 portrait surface = cairocffi.PDFSurface(filename, width, height) context = cairocffi.Context(surface) context.translate(0, height / 2) context.rotate(-0.1) layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(width)) layout.set_alignment(pangocffi.Alignment.CENTER) layout.set_markup('<span font="italic 30">Hi from Παν語</span>') pangocairocffi.show_layout(context, layout) surface.finish()
def test_tabs_replace() -> None: """Checks whether are there in end svg image. Pango should handle tabs and line breaks.""" size = 1 temp_pango_text = Text("hello\thi\nf") assert temp_pango_text.text == "hellohif" surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH) context = cairocffi.Context(surface) context.move_to(START_X, START_Y) layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(WIDTH)) fontdesc = pangocffi.FontDescription() fontdesc.set_size(pangocffi.units_from_double(size * 10)) layout.set_font_description(fontdesc) layout.set_text("hellohif") pangocairocffi.show_layout(context, layout) surface.finish() assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
def test_rtl_text_to_svgobject() -> None: """Checks number of submobjects generated when directly called using ``SVGMobject``""" size = 1 text = RTL_TEXT.replace("\n", "") temp_pango_text = Text(text, size=1) surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH) context = cairocffi.Context(surface) context.move_to(START_X, START_Y) layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(WIDTH)) fontdesc = pangocffi.FontDescription() fontdesc.set_size(pangocffi.units_from_double(size * 10)) layout.set_font_description(fontdesc) layout.set_text(text) pangocairocffi.show_layout(context, layout) surface.finish() assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
def get_layout(self, text: str, ctx: Optional[Context] = None, color: Color = Color.WHITE, escape_text=True)\ -> Layout: """ :param text: Text to layout. :param ctx: If None, a dummy context will be created. :param color: Color of the text. :param escape_text: If True, control characters will be macerated. :return: A pango layout object that can be rendered on the screen. """ if ctx is None: ctx = Context(ImageSurface(cairo.FORMAT_RGB16_565, 1, 1)) layout = pangocairo.create_layout(ctx) style = '"italic"' if self.italic else '"normal"' weight = '"bold"' if self.bold else '"normal"' layout.set_markup(f'<span font_family={quoteattr(self.name)} size={quoteattr(str(round(self.size * 1000)))} ' f'foreground={quoteattr(color.to_hex())} style={style} ' f'weight={weight}>{escape(text) if escape_text else text}</span>') return layout
def test_font_face() -> None: """Checks font face using submobject len""" size = 1 text = RTL_TEXT.replace("\n", "") font_face = "sans" temp_pango_text = Text(text, size=1, font=font_face) surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH) context = cairocffi.Context(surface) context.move_to(START_X, START_Y) layout = pangocairocffi.create_layout(context) layout.set_width(pangocffi.units_from_double(WIDTH)) fontdesc = pangocffi.FontDescription() fontdesc.set_family(font_face) fontdesc.set_size(pangocffi.units_from_double(size * 10)) layout.set_font_description(fontdesc) layout.set_text(text) pangocairocffi.show_layout(context, layout) surface.finish() assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
def test_compute_baseline(self): surface, cairo_context = self._create_real_surface( 'upright_text_path_compute_baseline.svg') layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('<span font="8">Hi from Παν語</span>') line_string = LineString([[0, 10], [50, 20], [100, 10]]) text_path = UprightTextPath(line_string, layout) text_path.alignment = Alignment.CENTER text_path.draw(cairo_context) baseline = text_path.compute_baseline() debug.draw_line_string(cairo_context, baseline) cairo_context.stroke() assert isinstance(baseline, LineString) assert baseline.length > 0 assert baseline.length < line_string.length surface.finish()
def test_draw(self): surface, cairo_context = self._create_real_surface('draw.svg') layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[10, 30], [90, 30]]) text_path = TextPath(line_string, layout) text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() line_string = LineString([[10, 50], [50, 50], [90, 90]]) text_path = TextPath(line_string, layout) text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() surface.finish()
def test_side(self): surface, cairo_context = self._create_real_surface('side.svg') layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[10, 30], [50, 30], [90, 70]]) text_path = TextPath(line_string, layout) text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() line_string = translate(line_string, 0, 20) text_path = TextPath(line_string, layout) text_path.side = Side.RIGHT text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() surface.finish()
def test_alignment(self): surface, cairo_context = self._create_real_surface( 'alignment.svg', 300, 100 ) layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('<span font="8">Hi from Παν語</span>') start_offsets = [-10, 0, 10] alignments = [Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT] for start_offset_index, start_offset in enumerate(start_offsets): for alignment_index, alignment in enumerate(alignments): line_string = LineString([ [5, 33 + alignment_index * 20], [30, 23 + alignment_index * 20], [50, 20 + alignment_index * 20], [70, 23 + alignment_index * 20], [95, 33 + alignment_index * 20] ]) line_string = translate( line_string, start_offset_index * 100, 0 ) text_path = TextPath(line_string, layout) text_path.alignment = alignment text_path.start_offset = start_offset cairo_context.set_source_rgb(0, 0, 0) text_path.draw(cairo_context) cairo_context.set_source_rgba(0, 0, 0, 0.2) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() surface.finish()
def test_setters_getters(self): surface, cairo_context = self._create_void_surface() layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[0, 0], [600, 0]]) text_path = TextPath(line_string, layout) assert isinstance(text_path.side, Side) assert isinstance(text_path.alignment, Alignment) assert isinstance(text_path.start_offset, float) assert isinstance(text_path.vertical_offset, float) text_path.side = Side.RIGHT text_path.alignment = Alignment.RIGHT text_path.start_offset = 12 text_path.vertical_offset = 34 assert isinstance(text_path.side, Side) assert isinstance(text_path.alignment, Alignment) assert isinstance(text_path.start_offset, float) assert isinstance(text_path.vertical_offset, float) text_path.layout_engine_class = Svg
def test_alignment_when_no_space(self): surface, cairo_context = self._create_real_surface( 'alignment_with_no_space.svg' ) layout = pangocairocffi.create_layout(cairo_context) layout.set_markup('Hi from Παν語') line_string = LineString([[10, 30], [90, 30]]) text_path = TextPath(line_string, layout) text_path.alignment = Alignment.CENTER text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() line_string = translate(line_string, 0, 25) text_path = TextPath(line_string, layout) text_path.alignment = Alignment.RIGHT text_path.draw(cairo_context) debug.draw_line_string(cairo_context, line_string) cairo_context.stroke() surface.finish()
def __init__(self, canvas: Canvas): self._layout = pangocairocffi.create_layout(canvas.context) self._position = CanvasCoordinate.origin() self._color = (0, 0, 0, 1)
def get_gudgitters_image(rankings): """return PIL image for rankings""" SMOKE_WHITE = (250, 250, 250) BLACK = (0, 0, 0) DISCORD_GRAY = (.212, .244, .247) ROW_COLORS = ((0.95, 0.95, 0.95), (0.9, 0.9, 0.9)) WIDTH = 900 HEIGHT = 450 BORDER_MARGIN = 20 COLUMN_MARGIN = 10 HEADER_SPACING = 1.25 WIDTH_RANK = 0.08 * WIDTH WIDTH_NAME = 0.38 * WIDTH LINE_HEIGHT = (HEIGHT - 2 * BORDER_MARGIN) / (10 + HEADER_SPACING) # Cairo+Pango setup surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) context = cairo.Context(surface) context.set_line_width(1) context.set_source_rgb(*DISCORD_GRAY) context.rectangle(0, 0, WIDTH, HEIGHT) context.fill() layout = PangoCairo.create_layout(context) layout.set_font_description( Pango.font_description_from_string(','.join(FONTS) + ' 20')) layout.set_ellipsize(Pango.EllipsizeMode.END) def draw_bg(y, color_index): nxty = y + LINE_HEIGHT # Simple context.move_to(BORDER_MARGIN, y) context.line_to(WIDTH, y) context.line_to(WIDTH, nxty) context.line_to(0, nxty) context.set_source_rgb(*ROW_COLORS[color_index]) context.fill() def draw_row(pos, username, handle, rating, color, y, bold=False): context.set_source_rgb(*[x / 255.0 for x in color]) context.move_to(BORDER_MARGIN, y) def draw(text, width=-1): text = html.escape(text) if bold: text = f'<b>{text}</b>' layout.set_width( (width - COLUMN_MARGIN) * 1000) # pixel = 1000 pango units layout.set_markup(text, -1) PangoCairo.show_layout(context, layout) context.rel_move_to(width, 0) draw(pos, WIDTH_RANK) draw(username, WIDTH_NAME) draw(handle, WIDTH_NAME) draw(rating) # y = BORDER_MARGIN # draw header draw_row('#', 'Name', 'Handle', 'Rating', SMOKE_WHITE, y, bold=True) y += LINE_HEIGHT * HEADER_SPACING for i, (pos, name, handle, rating) in enumerate(rankings): color = rating_to_color(rating) draw_bg(y, i % 2) draw_row(str(pos), name, handle, str(rating), color, y) if rating != 'N/A' and rating >= 3000: # nutella draw_row('', name[0], handle[0], '', BLACK, y) y += LINE_HEIGHT image_data = io.BytesIO() surface.write_to_png(image_data) image_data.seek(0) discord_file = discord.File(image_data, filename='gudgitters.png') return discord_file
def test_pdf(): filename = 'tests/output/extents.pdf' pt_per_mm = 72 / 25.4 width, height = 210 * pt_per_mm, 400 * pt_per_mm # A4 portrait surface = cairocffi.PDFSurface(filename, width, height) ctx = cairocffi.Context(surface) ctx.translate(width / 2, 10) layout = pangocairocffi.create_layout(ctx) layout.set_width(pangocffi.units_from_double(width / 2)) layout.set_alignment(pangocffi.Alignment.CENTER) layout.set_markup('<span font="italic 30">Hi from Παν語</span>\n' 'The text layout engine library for displaying ' '<span font-weight="bold">multi-language</span> text!') translate_y = 110 label_pos = (-width / 2 + 30, 30) label_width = width / 2.5 _show_layout_logical_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout (logical extents)') ctx.translate(0, translate_y) _show_layout_ink_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout (ink extents)') ctx.translate(0, translate_y) _show_layout_line_logical_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout Line (logical extents)') ctx.translate(0, translate_y) _show_layout_line_ink_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout Line (ink extents)') ctx.translate(0, translate_y) _show_layout_baseline(ctx, layout) _show_layout(ctx, layout) _show_label( ctx, label_pos, label_width, 'y-range (top), Baseline,\ny-range (bottom)' ) ctx.translate(0, translate_y) _show_run_logical_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout Run (logical extents)') ctx.translate(0, translate_y) _show_run_ink_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Layout Run (ink extents)') ctx.translate(0, translate_y) _show_cluster_logical_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Cluster (logical extents)') ctx.translate(0, translate_y) _show_cluster_ink_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Cluster (ink extents)') ctx.translate(0, translate_y) _show_character_extents(ctx, layout) _show_layout(ctx, layout) _show_label(ctx, label_pos, label_width, 'Character extents') ctx.translate(0, translate_y) surface.finish()
def createLayout(self, text: str) -> pango.Layout: layout = pangocairo.create_layout(self.context) layout.set_font_description(self._getFont()) layout.set_markup(fixMarkup(text)) return layout