def render(self, text): ch = ctypes.create_unicode_buffer(text) len_ch = len(text) # Layout rectangle; not clipped against so not terribly important. width = 10000 height = self._bitmap_height rect = Rectf( 0, self._bitmap_height - self.font.ascent + self.font.descent, width, height) # Set up GenericTypographic with 1 character measure range generic = ctypes.c_void_p() gdiplus.GdipStringFormatGetGenericTypographic(ctypes.byref(generic)) format = ctypes.c_void_p() gdiplus.GdipCloneStringFormat(generic, ctypes.byref(format)) gdiplus.GdipDeleteStringFormat(generic) # Measure advance # XXX HACK HACK HACK # Windows GDI+ is a filthy broken toy. No way to measure the bounding # box of a string, or to obtain LSB. What a joke. # # For historical note, GDI cannot be used because it cannot composite # into a bitmap with alpha. # # It looks like MS have abandoned GDI and GDI+ and are finally # supporting accurate text measurement with alpha composition in .NET # 2.0 (WinForms) via the TextRenderer class; this has no C interface # though, so we're entirely screwed. # # So anyway, we first try to get the width with GdipMeasureString. # Then if it's a TrueType font, we use GetCharABCWidthsW to get the # correct LSB. If it's a negative LSB, we move the layoutRect `rect` # to the right so that the whole glyph is rendered on the surface. # For positive LSB, we let the renderer render the correct white # space and we don't pass the LSB info to the Glyph.set_bearings bbox = Rectf() flags = (StringFormatFlagsMeasureTrailingSpaces | StringFormatFlagsNoClip | StringFormatFlagsNoFitBlackBox) gdiplus.GdipSetStringFormatFlags(format, flags) gdiplus.GdipMeasureString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, ctypes.byref(bbox), None, None) lsb = 0 advance = int(math.ceil(bbox.width)) width = advance # This hack bumps up the width if the font is italic; # this compensates for some common fonts. It's also a stupid # waste of texture memory. if self.font.italic: width += width // 2 # Do not enlarge more than the _rect width. width = min(width, self._rect.Width) # GDI functions only work for a single character so we transform # grapheme \r\n into \r if text == '\r\n': text = '\r' abc = ABC() # Check if ttf font. if gdi32.GetCharABCWidthsW(self._dc, ord(text), ord(text), byref(abc)): lsb = abc.abcA if lsb < 0: # Negative LSB: we shift the layout rect to the right # Otherwise we will cut the left part of the glyph rect.x = -lsb width -= lsb # XXX END HACK HACK HACK # Draw character to bitmap gdiplus.GdipGraphicsClear(self._graphics, 0x00000000) gdiplus.GdipDrawString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, self._brush) gdiplus.GdipFlush(self._graphics, 1) gdiplus.GdipDeleteStringFormat(format) bitmap_data = BitmapData() gdiplus.GdipBitmapLockBits(self._bitmap, byref(self._rect), ImageLockModeRead, self._format, byref(bitmap_data)) # Create buffer for RawImage buffer = create_string_buffer(bitmap_data.Stride * bitmap_data.Height) memmove(buffer, bitmap_data.Scan0, len(buffer)) # Unlock data gdiplus.GdipBitmapUnlockBits(self._bitmap, byref(bitmap_data)) image = pyglet.image.ImageData(width, height, 'BGRA', buffer, -bitmap_data.Stride) glyph = self.font.create_glyph(image) # Only pass negative LSB info lsb = min(lsb, 0) glyph.set_bearings(-self.font.descent, lsb, advance) return glyph
def render(self, text): ch = ctypes.create_unicode_buffer(text) len_ch = len(text) # Layout rectangle; not clipped against so not terribly important. width = 10000 height = self._bitmap_height rect = Rectf( 0, self._bitmap_height - self.font.ascent + self.font.descent, width, height) # Set up GenericTypographic with 1 character measure range generic = ctypes.c_void_p() gdiplus.GdipStringFormatGetGenericTypographic(ctypes.byref(generic)) format = ctypes.c_void_p() gdiplus.GdipCloneStringFormat(generic, ctypes.byref(format)) # Measure advance bbox = Rectf() flags = (StringFormatFlagsMeasureTrailingSpaces | StringFormatFlagsNoClip | StringFormatFlagsNoFitBlackBox) gdiplus.GdipSetStringFormatFlags(format, flags) gdiplus.GdipMeasureString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, ctypes.byref(bbox), None, None) lsb = 0 advance = int(math.ceil(bbox.width)) # XXX HACK HACK HACK # Windows GDI+ is a filthy broken toy. No way to measure the bounding # box of a string, or to obtain LSB. What a joke. # # For historical note, GDI cannot be used because it cannot composite # into a bitmap with alpha. # # It looks like MS have abandoned GDI and GDI+ and are finally # supporting accurate text measurement with alpha composition in .NET # 2.0 (WinForms) via the TextRenderer class; this has no C interface # though, so we're entirely screwed. # # So anyway, this hack bumps up the width if the font is italic; # this compensates for some common fonts. It's also a stupid waste of # texture memory. width = advance if self.font.italic: width += width // 2 # XXX END HACK HACK HACK # Draw character to bitmap gdiplus.GdipGraphicsClear(self._graphics, 0x00000000) gdiplus.GdipDrawString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, self._brush) gdiplus.GdipFlush(self._graphics, 1) bitmap_data = BitmapData() gdiplus.GdipBitmapLockBits(self._bitmap, byref(self._rect), ImageLockModeRead, self._format, byref(bitmap_data)) # Create buffer for RawImage buffer = create_string_buffer(bitmap_data.Stride * bitmap_data.Height) memmove(buffer, bitmap_data.Scan0, len(buffer)) # Unlock data gdiplus.GdipBitmapUnlockBits(self._bitmap, byref(bitmap_data)) image = pyglet.image.ImageData(width, height, 'BGRA', buffer, -bitmap_data.Stride) glyph = self.font.create_glyph(image) glyph.set_bearings(-self.font.descent, lsb, advance) return glyph
def render(self, text): ch = ctypes.create_unicode_buffer(text) len_ch = len(text) # Layout rectangle; not clipped against so not terribly important. width = 10000 height = self._bitmap_height rect = Rectf( 0, self._bitmap_height - self.font.ascent + self.font.descent, width, height) # Set up GenericTypographic with 1 character measure range generic = ctypes.c_void_p() gdiplus.GdipStringFormatGetGenericTypographic(ctypes.byref(generic)) format = ctypes.c_void_p() gdiplus.GdipCloneStringFormat(generic, ctypes.byref(format)) gdiplus.GdipDeleteStringFormat(generic) # Measure advance # XXX HACK HACK HACK # Windows GDI+ is a filthy broken toy. No way to measure the bounding # box of a string, or to obtain LSB. What a joke. # # For historical note, GDI cannot be used because it cannot composite # into a bitmap with alpha. # # It looks like MS have abandoned GDI and GDI+ and are finally # supporting accurate text measurement with alpha composition in .NET # 2.0 (WinForms) via the TextRenderer class; this has no C interface # though, so we're entirely screwed. # # So anyway, we first try to get the lsb and width from GDI # GetCharABCWidthsW function. If it does not work (because we don't # use a TrueType font), we try to use GdipMeasureString. # Tests show that it's not working well for .fon fonts. # GDI functions only work for a single character so we transform # grapheme \r\n into \r if text == '\r\n': text = '\r' abc = ABC() # Check if ttf font. if gdi32.GetCharABCWidthsW(self._dc, ord(text), ord(text), byref(abc)): lsb = abc.abcA width = abc.abcB advance = abc.abcA + abc.abcB + abc.abcC rect.x = -lsb else: # What font could this be ??? # Revert to the old way to check bounding box. bbox = Rectf() flags = (StringFormatFlagsMeasureTrailingSpaces | StringFormatFlagsNoClip | StringFormatFlagsNoFitBlackBox) gdiplus.GdipSetStringFormatFlags(format, flags) gdiplus.GdipMeasureString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, ctypes.byref(bbox), None, None) lsb = 0 advance = int(math.ceil(bbox.width)) width = advance # XXX END HACK HACK HACK # Draw character to bitmap gdiplus.GdipGraphicsClear(self._graphics, 0x00000000) gdiplus.GdipDrawString(self._graphics, ch, len_ch, self.font._gdipfont, ctypes.byref(rect), format, self._brush) gdiplus.GdipFlush(self._graphics, 1) gdiplus.GdipDeleteStringFormat(format) bitmap_data = BitmapData() gdiplus.GdipBitmapLockBits(self._bitmap, byref(self._rect), ImageLockModeRead, self._format, byref(bitmap_data)) # Create buffer for RawImage buffer = create_string_buffer(bitmap_data.Stride * bitmap_data.Height) memmove(buffer, bitmap_data.Scan0, len(buffer)) # Unlock data gdiplus.GdipBitmapUnlockBits(self._bitmap, byref(bitmap_data)) image = pyglet.image.ImageData(width, height, 'BGRA', buffer, -bitmap_data.Stride) glyph = self.font.create_glyph(image) glyph.set_bearings(-self.font.descent, lsb, advance) return glyph