Example #1
0
    def calculate_metrics(self):
        # It seems the only way to get the font's ascent and descent is to lay
        # out some glyphs and measure them.

        # fake ucs2 string
        text = '\0a'

        layout = c_void_p()
        carbon.ATSUCreateTextLayout(byref(layout))
        carbon.ATSUSetTextPointerLocation(layout, text, kATSUFromTextBeginning,
                                          kATSUToTextEnd, 1)
        carbon.ATSUSetRunStyle(layout, self.atsu_style, kATSUFromTextBeginning,
                               kATSUToTextEnd)

        # determine the metrics for this font only
        carbon.ATSUSetTransientFontMatching(layout, False)

        value = ATSUTextMeasurement()
        carbon.ATSUGetLineControl(layout, 0, kATSULineAscentTag, sizeof(value),
                                  byref(value), None)
        self.ascent = int(math.ceil(fix2float(value)))
        carbon.ATSUGetLineControl(layout, 0, kATSULineDescentTag,
                                  sizeof(value), byref(value), None)
        self.descent = -int(math.ceil(fix2float(value)))
Example #2
0
    def render(self, text):
        # Convert text to UCS2
        text_len = len(text)
        text_ucs2 = str_ucs2(text)

        # Create layout override handler to extract device advance value.
        override_spec = ATSULayoutOperationOverrideSpecifier()
        override_spec.operationSelector = \
            kATSULayoutOperationPostLayoutAdjustment
        override_spec.overrideUPP = \
            ATSUDirectLayoutOperationOverrideUPP(self._layout_callback)

        # Create ATSU text layout for this text and font
        layout = c_void_p()
        carbon.ATSUCreateTextLayout(byref(layout))
        set_layout_attributes(
            layout, {
                kATSUCGContextTag: self._bitmap_context,
                kATSULayoutOperationOverrideTag: override_spec
            })
        carbon.ATSUSetTextPointerLocation(layout, text_ucs2,
                                          kATSUFromTextBeginning,
                                          kATSUToTextEnd, text_len)
        carbon.ATSUSetRunStyle(layout, self.font.atsu_style,
                               kATSUFromTextBeginning, kATSUToTextEnd)

        # Turning on transient font matching screws up font layout
        # predictability when strange fonts are installed
        # <ah> Don't believe this.  Can't get foreign/special characters
        #      without transient on.
        carbon.ATSUSetTransientFontMatching(layout, True)

        # Get bitmap dimensions required
        rect = Rect()
        carbon.ATSUMeasureTextImage(layout, kATSUFromTextBeginning,
                                    kATSUToTextEnd, 0, 0, byref(rect))
        image_width = rect.right - rect.left + 2
        image_height = rect.bottom - rect.top + 2
        baseline = rect.bottom + 1
        lsb = rect.left

        # Resize Quartz context if necessary
        if (image_width > self._bitmap_rect.size.width
                or image_height > self._bitmap_rect.size.height):
            self._create_bitmap_context(
                int(max(image_width, self._bitmap_rect.size.width)),
                int(max(image_height, self._bitmap_rect.size.height)))

            set_layout_attributes(layout,
                                  {kATSUCGContextTag: self._bitmap_context})

        # Draw to the bitmap
        carbon.CGContextClearRect(self._bitmap_context, self._bitmap_rect)
        carbon.ATSUDrawText(layout, 0, kATSUToTextEnd, fixed(-lsb + 1),
                            fixed(baseline))

        advance = self._glyph_advance

        # Round advance to nearest int.  It actually looks good with sub-pixel
        # advance as well -- Helvetica at 12pt is more tightly spaced, but
        # Times New Roman at 12pt is too light.  With integer positioning
        # overall look seems darker and perhaps more uniform.  It's also more
        # similar (programmatically) to Win32 and FreeType.  Still, worth
        # messing around with (comment out next line) if you're interested.
        advance = int(round(advance))

        # Fix advance for zero-width space
        if text == u'\u200b':
            advance = 0

        # A negative pitch is required, but it is much faster to load the
        # glyph upside-down and flip the tex_coords.  Note region used
        # to start at top of glyph image.
        pitch = int(4 * self._bitmap_rect.size.width)
        image = pyglet.image.ImageData(image_width,
                                       self._bitmap_rect.size.height, 'RGBA',
                                       self._bitmap, pitch)
        skip_rows = int(self._bitmap_rect.size.height - image_height)
        image = image.get_region(0, skip_rows, image.width, image_height)
        glyph = self.font.create_glyph(image)
        glyph.set_bearings(baseline, lsb - 1, int(advance))
        t = list(glyph.tex_coords)
        glyph.tex_coords = t[9:12] + t[6:9] + t[3:6] + t[:3]

        return glyph
Example #3
0
    def render(self, text):
        # Convert text to UCS2
        text_len = len(text)
        text = str_ucs2(text)

        # Create ATSU text layout for this text and font
        layout = c_void_p()
        carbon.ATSUCreateTextLayout(byref(layout))
        set_layout_attributes(layout,
                              {kATSUCGContextTag: self._bitmap_context})
        carbon.ATSUSetTextPointerLocation(layout, text, kATSUFromTextBeginning,
                                          kATSUToTextEnd, text_len)
        carbon.ATSUSetRunStyle(layout, self.font.atsu_style,
                               kATSUFromTextBeginning, kATSUToTextEnd)

        # Turning on transient font matching screws up font layout
        # predictability when strange fonts are installed
        carbon.ATSUSetTransientFontMatching(layout, False)

        # Get bitmap dimensions required
        rect = Rect()
        carbon.ATSUMeasureTextImage(layout, kATSUFromTextBeginning,
                                    kATSUToTextEnd, 0, 0, byref(rect))
        image_width = rect.right - rect.left + 2
        image_height = rect.bottom - rect.top + 2
        baseline = rect.bottom + 1
        lsb = rect.left

        # Resize Quartz context if necessary
        if (image_width > self._bitmap_rect.size.width
                or image_height > self._bitmap_rect.size.height):
            self._create_bitmap_context(
                int(max(image_width, self._bitmap_rect.size.width)),
                int(max(image_height, self._bitmap_rect.size.height)))

            set_layout_attributes(layout,
                                  {kATSUCGContextTag: self._bitmap_context})

        # Get typographic box, which gives advance.
        bounds_actual = c_uint32()
        bounds = ATSTrapezoid()
        carbon.ATSUGetGlyphBounds(layout, 0, 0, kATSUFromTextBeginning,
                                  kATSUToTextEnd, kATSUseDeviceOrigins, 1,
                                  byref(bounds), byref(bounds_actual))
        advance = fix2float(bounds.lowerRight.x) - fix2float(
            bounds.lowerLeft.x)

        # Draw to the bitmap
        carbon.CGContextClearRect(self._bitmap_context, self._bitmap_rect)
        carbon.ATSUDrawText(layout, 0, kATSUToTextEnd, fixed(-lsb + 1),
                            fixed(baseline))

        # A negative pitch is required, but it is much faster to load the
        # glyph upside-down and flip the tex_coords.  Note region used
        # to start at top of glyph image.
        pitch = int(4 * self._bitmap_rect.size.width)
        image = pyglet.image.ImageData(image_width,
                                       self._bitmap_rect.size.height, 'RGBA',
                                       self._bitmap, pitch)
        skip_rows = int(self._bitmap_rect.size.height - image_height)
        image = image.get_region(0, skip_rows, image.width, image_height)
        glyph = self.font.create_glyph(image)
        glyph.set_bearings(baseline, lsb - 1, int(advance))
        t = list(glyph.tex_coords)
        glyph.tex_coords = t[9:12] + t[6:9] + t[3:6] + t[:3]

        return glyph