def set_best_font(self): text_fits = 0 while True: text_fits += 1 font = skia.Font(skia.Typeface('Ubuntu'), text_fits) height_size = font.getSpacing() text_size = font.measureText("F#") if text_size > self.semitone_width: break self.font = skia.Font(skia.Typeface('Arial'), text_fits - 4)
def anchored_text(self, canvas, text, x: int, y: int, anchor_x: float, anchor_y: float, **kwargs): # If user sent a single string, it means a 1 item list if not isinstance(text, list): text = [text] # Get paint, font paint = kwargs.get("paint", skia.Paint(AntiAlias=True, Color=skia.ColorWHITE)) font = kwargs.get("font", skia.Font(skia.Typeface('Arial'), 24)) # Width and height of text height_size = font.getSpacing() for line_number, text_string in enumerate(text): text_size = font.measureText(text_string) # Where we'll plot the top left text according to the anchor and whatnot real_x = x - (text_size * anchor_x) real_y = y - (height_size * anchor_y) + height_size + (line_number * height_size) canvas.drawString(text_string, real_x, real_y, font, paint)
def _getTypefaceAndTTFont(fontNameOrPath): cacheKey = fontNameOrPath if cacheKey not in _fontCache: if fontNameOrPath is None: typeface = skia.Typeface(None) else: fontNameOrPath = os.fspath(fontNameOrPath) if not os.path.exists(fontNameOrPath): typeface = skia.Typeface(fontNameOrPath) else: typeface = skia.Typeface.MakeFromFile(fontNameOrPath) if typeface is None: raise DrawbotError(f"can't load font: {fontNameOrPath}") ttFont = makeTTFontFromSkiaTypeface(typeface) _fontCache[cacheKey] = typeface, ttFont return _fontCache[cacheKey]
def draw_text(self, canvas, text_line, x_pos, y_pos, text_style=None): """ """ ## Create a list to hold Typeface constructor args typeface_args = [text_line.font] ## Append FontStyle objects to list depending on params if text_line.italic and text_line.bold: typeface_args.append(skia.FontStyle.BoldItalic()) elif text_line.bold: typeface_args.append(skia.FontStyle.Bold()) elif text_line.italic: typeface_args.append(skia.FontStyle.Italic()) ## Create FontStyle object from constructor args typeface = skia.Typeface(*typeface_args) ## Create a font and set params font = skia.Font() font.setSize(float(text_line.size)) font.setTypeface(typeface) ## Create a paint object paint = skia.Paint(self.cache_color(text_line.color)) if text_style: width = font.measureText(text_line.text) height = font.getSize() * 1.334 #! I really hate this hack x_pos -= width / 2 y_pos += height / 2 ## Draw Text Line! canvas.drawSimpleText(text_line.text, x_pos, y_pos, font, paint)
def setFont(self, fontNameOrPath): if os.path.exists(fontNameOrPath): path = os.path.normpath(os.path.abspath(os.fspath(fontNameOrPath))) tf = self._getFontFromFile(path) else: tf = skia.Typeface(fontNameOrPath) self.font.setTypeface(tf) # purge cached items: self._ttFont = None self._shape = None
def __init__(self, cachedTypefaces, _doInitialize=True): self._cachedTypefaces = cachedTypefaces self._ttFont = None self._shape = None if not _doInitialize: # self.copy() will initialize further return self.font = skia.Font(skia.Typeface(None), 10) self.font.setForceAutoHinting(False) self.font.setHinting(skia.FontHinting.kNone) self.font.setSubpixel(True) self.font.setEdging(skia.Font.Edging.kAntiAlias) self.features = {} self.variations = {}
def draw_text(text: str, point: tuple[float, float], color, *, size=40, font='Arial', align_x=Align.CENTER, align_y=Align.CENTER): if align_x not in (Align.LEFT, Align.CENTER, Align.RIGHT): raise ValueError(f"align_x bad value {align_x!r}.\nValid values are: {', '.join(map(repr, [Align.LEFT, Align.CENTER, Align.RIGHT]))}.") if align_y not in (Align.TOP, Align.CENTER, Align.BOTTOM): raise ValueError(f"align_y bad value {align_x!r}.\nValid values are: {', '.join(map(repr, [Align.TOP, Align.CENTER, Align.BOTTOM]))}.") try: cache = globals()['_text_cache'] except: cache = globals()['_text_cache'] = {} try: blob, offsets = cache[(text, font, size)] except: sfont = skia.Font(skia.Typeface(font), size) blob: skia.TextBlob = skia.TextBlob.MakeFromText(text, sfont) glyphs = sfont.textToGlyphs(text) bounds = sfont.getBounds(glyphs) positions = sfont.getXPos(glyphs) min_y = min(-b.fBottom for b in bounds) max_y = max(-b.fTop for b in bounds) # take the average of the centers as the text center center_y = sum(-b.centerY() for b in bounds) / len(bounds) min_x = positions[0] + bounds[0].fLeft max_x = positions[-1] + bounds[-1].fRight center_x = (min_x + max_x) / 2 offsets = {'min_x': min_x, 'center_x': center_x, 'max_x': max_x, 'min_y': min_y, 'center_y': center_y, 'max_y': max_y} cache[(text, font, size)] = blob, offsets x, y = point if align_x == 'left': x -= offsets['max_x'] elif align_x == 'center': x -= offsets['center_x'] elif align_x == 'right': x -= offsets['min_x'] if align_y == 'bottom': y += offsets['max_y'] elif align_y == 'center': y += offsets['center_y'] elif align_y == 'top': y += offsets['min_y'] _canvas.drawTextBlob(blob, x, y, Color._paint(color))
def draw(pen, state, data): if state != 0: return if not pen.visible: return if isinstance(pen, DATText): if isinstance(pen.style.font, str): font = skia.Typeface(pen.style.font) else: font = skia.Typeface.MakeFromFile(str(pen.style.font.path)) if len(pen.style.variations) > 0: fa = skia.FontArguments() # h/t https://github.com/justvanrossum/drawbot-skia/blob/master/src/drawbot_skia/gstate.py to_int = lambda s: struct.unpack( ">i", bytes(s, "ascii"))[0] makeCoord = skia.FontArguments.VariationPosition.Coordinate rawCoords = [ makeCoord(to_int(tag), value) for tag, value in pen.style.variations.items() ] coords = skia.FontArguments.VariationPosition.Coordinates( rawCoords) fa.setVariationDesignPosition( skia.FontArguments.VariationPosition(coords)) font = font.makeClone(fa) pt = pen.frame.point("SW") canvas.drawString( pen.text, pt.x, rect.h - pt.y, skia.Font(font, pen.style.fontSize), skia.Paint(AntiAlias=True, Color=pen.style.fill.skia())) if state == 0: SkiaPen(pen, rect, canvas, scale, style=style, alpha=pen.calc_alpha())
0, # stencilBits skia.GrGLFramebufferInfo(0, GL.GL_RGBA8)) surface = skia.Surface.MakeFromBackendRenderTarget( context, backend_render_target, skia.kBottomLeft_GrSurfaceOrigin, skia.kRGBA_8888_ColorType, skia.ColorSpace.MakeSRGB()) assert surface is not None yield surface context.abandonContext() db = sqlite_utils.Database(':memory:') db['gui_status'].insert_all([{'name': 'scroll_x', 'value': 0}, {'name': 'scroll_y', 'value': 0}], pk='name') db['widget'].create({'text': str, 'x': int, 'y': int, 'width': int, 'height': int}) typeface = skia.Typeface('Arial') font = skia.Font(typeface, 14, 1, 0) font.setEdging(skia.Font.Edging.kAntiAlias) def add_widget(text): y = (db.execute('select max(y) from widget').fetchone()[0] or 0) + 30 x = 10 db['widget'].insert({'text': text, 'x': x, 'y': y, 'width': 100, 'height': 20}) with glfw_window() as window: GL.glClear(GL.GL_COLOR_BUFFER_BIT) def scroll_callback(_win, dx, dy): new_scroll_x = db['gui_status'].get('scroll_x')['value'] + dx db['gui_status'].update('scroll_x', {'value': new_scroll_x})
import glfw # type: ignore import skia # type: ignore import traceback import random import math import ast import importlib from dataclasses import dataclass from OpenGL import GL # type: ignore from typing import List from functools import lru_cache, wraps import numpy as np # type: ignore import matplotlib # type: ignore from buffer import Buffer font = skia.Font(skia.Typeface('Liberation Mono'), 14) line_height = font.getSpacing() col_width = font.getWidths([ord('x')])[0] @dataclass class Cell: input: Buffer output: object target_scroll = [0, 0] def clamp(l, u, v): return max(min(v, u), l)
def run(self): # Calculate fps? if self.pyskt_context.show_debug: frame_times = [0] * 120 frame = 0 last_time_completed = time.time() # Link events to parsers glfw.set_window_size_callback(self.window, self.events.on_window_resize) glfw.set_mouse_button_callback(self.window, self.events.mouse_callback) glfw.set_scroll_callback(self.window, self.events.mouse_callback) glfw.set_key_callback(self.window, self.events.keyboard_callback) glfw.set_drop_callback(self.window, self.events.on_file_drop) scroll_text_x = self.pyskt_context.width // 2 scroll_text_y = self.pyskt_context.height // 2 wants_to_go = [scroll_text_x, scroll_text_y] threading.Thread(target=self.events_loop).start() # Loop until the user closes the window while not glfw.window_should_close(self.window): # Wait events if said to # if self.pyskt_context.wait_events: # Clear canvas self.canvas.clear(self.colors.background) # Get mouse position self.mouse_pos = glfw.get_cursor_pos(self.window) self.check_mouse_moved() if self.events.left_click: wants_to_go[0] -= self.events.scroll * 50 else: wants_to_go[1] -= self.events.scroll * 50 # We have now to recursively search through the components dictionary scroll_text_x = scroll_text_x + (wants_to_go[0] - scroll_text_x) * 0.3 scroll_text_y = scroll_text_y + (wants_to_go[1] - scroll_text_y) * 0.3 self.draw_utils.anchored_text( canvas=self.canvas, text="A Really long and centered text", x=scroll_text_x, y=scroll_text_y, anchor_x=0.5, anchor_y=0.5, ) # Hover testing for _ in range(20): random.seed(_) xywh_rect = [ random.randint(0, 1000), random.randint(0, 1000), random.randint(0, 400), random.randint(0, 400) ] # Rectangle border # rect = self.pyskt_processing.rectangle_x_y_w_h_to_coordinates(*xywh_rect) info = self.pyskt_processing.point_against_rectangle( self.mouse_pos, xywh_rect) if info["is_inside"]: paint = skia.Paint( AntiAlias=True, Color=skia.Color4f(1, 0, 1, 1), Style=skia.Paint.kFill_Style, StrokeWidth=2, ) else: c = 1 - (info["distance"] / 1080) paint = skia.Paint( AntiAlias=True, Color=skia.Color4f(c, c, c, 1), Style=skia.Paint.kFill_Style, StrokeWidth=2, ) # Draw the border self.canvas.drawRect( skia.Rect( *self.pyskt_processing.rectangle_x_y_w_h_to_skia_rect( *xywh_rect)), paint) # # # # Show fps if self.pyskt_context.show_debug: frame_times[frame % 120] = time.time() - last_time_completed absolute_frame_times = [x for x in frame_times if not x == 0] fps = 1 / (sum(absolute_frame_times) / len(absolute_frame_times)) last_time_completed = time.time() frame += 1 self.draw_utils.anchored_text( canvas=self.canvas, text=[f"{fps=:.1f}", f"mouse_pos={self.mouse_pos}"], x=0, y=0, anchor_x=0, anchor_y=0, # kwargs font=skia.Font(skia.Typeface('Arial'), 12), ) # If any event doesn't return to "None" state self.events.reset_non_ending_states() # Flush buffer self.surface.flushAndSubmit() # Swap front and back buffers glfw.swap_buffers(self.window) # Poll for and process events glfw.poll_events() # End glfw glfw.terminate()