Re: How do I get X, Y pos and PangoLogAttr of each glyph in a rendered string?
- From: Behdad Esfahbod <behdad behdad org>
- To: Alex Kerr <alex phonething com>
- Cc: gtk-i18n-list gnome org
- Subject: Re: How do I get X, Y pos and PangoLogAttr of each glyph in a rendered string?
- Date: Wed, 20 Apr 2011 01:11:13 -0400
On 04/19/11 16:27, Alex Kerr wrote:
> Hello,
Hi Alex,
> I'm using a really basic Cairo Pango prog as a test bed. It just displays a
> short string on a Cairo surface using a Pango layout, which works fine.
>
> For each glyph in the string, I now want to get it's X,Y pos (and width and
> height) on the rendered Cairo surface, and the PangoLogAttr data, e.g. so I
> could draw a box around each glyph, or whatever.
>
> After a lot of reading the API stuff, and googling for examples, I'm not quite
> sure how to tie the API functions together to achieve this. Anyone got any
> suggestions, ideas, or code please?
You definitely can do it with glyphs, but is that really what you want?
There's simpler API in pango to do that per cluster. Note that you cannot map
a glyph to the original string (and hence PangoLogAttr). You can only do that
per cluster.
Check PangoLayoutIter.
I'm also attaching Python code that draws boxes around clusters.
Hope that helps,
behdad
> Pseudocode for what I'm trying to do would be:
>
> 1. Render Pango text to Cairo surface (note: could be right-to-left, e.g.
> Arabic, as well as left-to-right) - Done this.
>
> 2. For each glyph (or ligature) in the line of text:
> {
> 3. Get X, Y, Width and Height of rendered glyph (or ligature)
> 4. Get the PangoLogAttr structure for the same character
> 5. Move onto the next glyph (/ligature/character) in the line
> }
>
> P.S. As a separate side note, I understand glyphs to be the separate
> components of a final rendered character, i.e. the main body and diacritic (if
> present) would be separate glyphs. Each glyph in turn could possibly be made
> up of more than one UTF-8 character. The rendering engine can potentially
> combine certain combinations of glyphs to form a single ligature. Glyphs or
> ligatures can also be referred to as characters (so PangoLogAttr applies to
> ligatures and glyphs?). Have I got this right!?
>
> Any help much appreciated :)
>
> Many thanks indeed!
> Alex
>
>
>
> _______________________________________________
> gtk-i18n-list mailing list
> gtk-i18n-list gnome org
> http://mail.gnome.org/mailman/listinfo/gtk-i18n-list
>
#!/usr/bin/python
# -*- coding:utf8 -*-
#
# I, Adam Olsen, am the original author of this work. I hereby
# donate it into the public domain, and relinquish any rights I
# may have in it.
#
# I, Behdad Esfahbod, hereby disclaim any rights for my contributions
# to this code.
from __future__ import division
import sys
import cairo
import pygtk
pygtk.require('2.0')
import gtk
import gtk.gdk
import pango
import gobject
def generate_modes():
for justify_desc, justify in [('', False), ('justified ', True)]:
for align_desc, align in [('left', pango.ALIGN_LEFT),
('center', pango.ALIGN_CENTER), ('right', pango.ALIGN_RIGHT)]:
for extent_desc, extentindex in [('logical', 1), ('ink', 0)]:
for name in ['line', 'run', 'cluster', 'char']:
if name == 'char' and extent_desc == 'ink':
continue
desc = '%s%s %s %s' % (justify_desc, align_desc, extent_desc, name)
yield extentindex, name, align, justify, desc
class ExtentDemo(gtk.Widget):
def __init__(self, text="""Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor.\n\tسÙ?اÙ?Û? Ú?Ù? بÙ?Û? Ø®Ù?Ø´Ù? آشÙ?اÛ?Û?... بر Ø¢Ù? Ù?Ù?Ù?تÙ? دÛ?راÙ?â??داÙ? دÛ?راÙ?..."""):
gtk.Widget.__init__(self)
self.text = "foo"
self.text = text
self.all_modes = list(generate_modes())
self.mode_num = 0;
self.x_margin = 5
self.y_margin = 5
self.x_offset = 0
self.y_offset = 25
self.font_size = 24
def do_realize(self):
self.set_flags(self.flags() | gtk.REALIZED)
self.window = gtk.gdk.Window(
self.get_parent_window(),
width=self.allocation.width,
height=self.allocation.height,
window_type=gtk.gdk.WINDOW_CHILD,
wclass=gtk.gdk.INPUT_OUTPUT,
event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK)
self.window.set_user_data(self)
self.style.attach(self.window)
self.style.set_background(self.window, gtk.STATE_NORMAL)
self.window.move_resize(*self.allocation)
def do_unrealize(self):
self.window.destroy()
def do_size_request(self, requisition):
width = 800
layout = self.get_layout(self.get_pango_context())
layout.set_width (pango.SCALE * (width - (self.x_offset + 2 * self.x_margin)))
height = layout.get_pixel_extents ()[1][3] + (self.y_offset + 2 * self.y_margin)
requisition.width = width
requisition.height = height
def do_expose_event(self, event):
context = self.window.cairo_create()
context.rectangle(event.area.x, event.area.y,
event.area.width, event.area.height)
context.clip()
pangocontext = self.get_pango_context()
self.draw(context, pangocontext)
return False
def get_layout (self, pangocontext):
font = pango.FontDescription()
font.set_family("sans")
font.set_size(self.font_size * pango.SCALE)
layout = pango.Layout(pangocontext)
layout.set_font_description(font)
layout.set_markup(self.text)
return layout
def draw(self, context, pangocontext):
context.set_source_rgb (1, 1, 1)
context.paint()
context.set_source_rgb (0, 0, 0)
context.translate (self.x_margin, self.y_margin)
extentindex, name, align, justify, desc = self.all_modes[self.mode_num]
labellayout = pango.Layout(pangocontext)
labellayout.set_text('%i: %s' % (self.mode_num + 1, desc))
context.move_to(0, 0)
context.show_layout(labellayout)
context.translate (self.x_offset, self.y_offset)
layout = self.get_layout (pangocontext)
width = self.allocation.width - (self.x_offset + 2 * self.x_margin)
layout.set_width(width * pango.SCALE)
layout.set_alignment(align)
layout.set_justify(justify)
context.move_to(0, 0)
#context.layout_path(layout)
#context.fill()
context.show_layout(layout)
context.set_source_rgba(1, 0, 0, 0.5)
context.set_line_width (2)
x, y, width, height = layout.get_pixel_extents()[extentindex]
context.rectangle(x-1, y-1, width+2, height+2)
context.stroke()
context.set_source_rgba(0, 1, 0, 0.7)
context.set_line_width (1)
li = layout.get_iter()
while True:
extents = getattr(li, 'get_%s_extents' % name)()
if name != 'char':
extents = extents[extentindex]
x, y, width, height = self._descale(extents)
context.rectangle(x+.5, y+.5, width-1, height-1)
context.stroke()
if not getattr(li, 'next_%s' % name)():
break
def cycle_mode_forward(self):
self.mode_num += 1
if self.mode_num >= len(self.all_modes):
self.mode_num = 0
self.queue_draw()
def cycle_mode_backward(self):
self.mode_num -= 1
if self.mode_num < 0:
self.mode_num = len(self.all_modes) - 1
self.queue_draw()
def key_press_event(self, widget, event):
if event.string == ' ' or event.keyval == gtk.keysyms.Right:
self.cycle_mode_forward()
elif event.keyval == gtk.keysyms.BackSpace or event.keyval == gtk.keysyms.Left:
self.cycle_mode_backward()
elif event.string == 'q':
gtk.main_quit()
def _descale(self, rect):
return (i / pango.SCALE for i in rect)
def run(self):
window = gtk.Window()
window.add(self)
window.connect("destroy", gtk.main_quit)
window.connect("key-press-event", self.key_press_event)
window.show_all()
gtk.main()
gobject.type_register(ExtentDemo)
def main():
if len (sys.argv) > 2:
ed = ExtentDemo(sys.argv[2])
else:
ed = ExtentDemo()
if len (sys.argv) > 1:
mode = int(sys.argv[1])
while mode > 1:
mode -= 1
ed.cycle_mode_forward()
ed.run()
if __name__ == "__main__":
main()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]