This file is indexed.

/usr/share/pyshared/igraph/drawing/text.py is in python-igraph 0.6.5-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
"""
Drawers for labels on plots.

@undocumented: test
"""

import re

from igraph.compat import property
from igraph.drawing.baseclasses import AbstractCairoDrawer
from warnings import warn

__all__ = ["TextDrawer"]
__license__ = "GPL"

__docformat__ = "restructuredtext en" 

#####################################################################

class TextDrawer(AbstractCairoDrawer):
    """Class that draws text on a Cairo context.
    
    This class supports multi-line text unlike the original Cairo text
    drawing methods."""

    LEFT, CENTER, RIGHT = "left", "center", "right"
    TOP, BOTTOM = "top", "bottom"

    def __init__(self, context, text="", halign="center", valign="center"):
        """Constructs a new instance that will draw the given `text` on
        the given Cairo `context`."""
        super(TextDrawer, self).__init__(context, (0, 0))
        self.text = text
        self.halign = halign
        self.valign = valign

    def draw(self, wrap=False):
        """Draws the text in the current bounding box of the drawer.
        
        Since the class itself is an instance of `AbstractCairoDrawer`, it
        has an attribute named ``bbox`` which will be used as a bounding
        box.
        
        :Parameters:
          wrap : boolean
            whether to allow re-wrapping of the text if it does not fit
            within the bounding box horizontally.
        """
        ctx = self.context
        bbox = self.bbox

        text_layout = self.get_text_layout(bbox.left, bbox.top, bbox.width, wrap)
        if not text_layout:
            return

        _, font_descent, line_height = ctx.font_extents()[:3]
        yb = ctx.text_extents(text_layout[0][2])[1]
        total_height = len(text_layout) * line_height

        if self.valign == self.BOTTOM:
            # Bottom vertical alignment
            dy = bbox.height - total_height - yb + font_descent
        elif self.valign == self.CENTER:
            # Centered vertical alignment
            dy = (bbox.height - total_height - yb + font_descent + line_height) / 2.
        else:
            # Top vertical alignment
            dy = line_height

        for ref_x, ref_y, line in text_layout:
            ctx.move_to(ref_x, ref_y + dy)
            ctx.show_text(line)
        ctx.new_path()

    def get_text_layout(self, x = None, y = None, width = None, wrap = False):
        """Calculates the layout of the current text. `x` and `y` denote the
        coordinates where the drawing should start. If they are both ``None``,
        the current position of the context will be used.

        Vertical alignment settings are not taken into account in this method
        as the text is not drawn within a box.
        
        :Parameters:
          x : float or ``None``
            The X coordinate of the reference point where the layout should
            start.
          y : float or ``None``
            The Y coordinate of the reference point where the layout should
            start.
          width : float or ``None``
            The width of the box in which the text will be fitted. It matters
            only when the text is right-aligned or centered. The text will
            overflow the box if any of the lines is longer than the box width
            and `wrap` is ``False``.
          wrap : boolean
            whether to allow re-wrapping of the text if it does not fit
            within the given width.

        :Returns:
          a list consisting of ``(x, y, line)`` tuples where ``x`` and ``y``
          refer to reference points on the Cairo canvas and ``line`` refers
          to the corresponding text that should be plotted there.
        """
        ctx = self.context

        if x is None or y is None:
            x, y = ctx.get_current_point()

        line_height = ctx.font_extents()[2]

        if wrap:
            if width and width > 0:
                iterlines = self._iterlines_wrapped(width)
            else:
                warn("ignoring wrap=True as no width was specified")
        else:
            iterlines = self._iterlines()

        result = []

        if self.halign == self.CENTER:
            # Centered alignment
            if width is None:
                width = self.text_extents()[2]
            for line, line_width, x_bearing in iterlines:
                result.append((x + (width-line_width)/2. - x_bearing, y, line))
                y += line_height

        elif self.halign == self.RIGHT:
            # Right alignment
            if width is None:
                width = self.text_extents()[2]
            x += width
            for line, line_width, x_bearing in iterlines:
                result.append((x - line_width - x_bearing, y, line))
                y += line_height

        else:
            # Left alignment
            for line, _, x_bearing in iterlines:
                result.append((x-x_bearing, y, line))
                y += line_height

        return result

    def draw_at(self, x = None, y = None, width = None, wrap = False):
        """Draws the text by setting up an appropriate path on the Cairo
        context and filling it. `x` and `y` denote the coordinates where the
        drawing should start. If they are both C{None}, the current position
        of the context will be used.
        
        Vertical alignment settings are not taken into account in this method
        as the text is not drawn within a box.
        
        :Parameters:
          x : float or ``None``
            The X coordinate of the reference point where the drawing should
            start.
          y : float or ``None``
            The Y coordinate of the reference point where the drawing should
            start.
          width : float or ``None``
            The width of the box in which the text will be fitted. It matters
            only when the text is right-aligned or centered. The text will
            overflow the box if any of the lines is longer than the box width.
          wrap : boolean
            whether to allow re-wrapping of the text if it does not fit
            within the given width.
        """
        ctx = self.context
        for ref_x, ref_y, line in self.get_text_layout(x, y, width, wrap):
            ctx.move_to(ref_x, ref_y)
            ctx.show_text(line)
        ctx.new_path()

    def _iterlines(self):
        """Iterates over the label line by line and returns a tuple containing
        the folloing for each line: the line itself, the width of the line and
        the X-bearing of the line."""
        ctx = self.context
        for line in self._text.split("\n"):
            xb, _, line_width, _, _, _ = ctx.text_extents(line)
            yield (line, line_width, xb)

    def _iterlines_wrapped(self, width):
        """Iterates over the label line by line and returns a tuple containing
        the folloing for each line: the line itself, the width of the line and
        the X-bearing of the line.
        
        The difference between this method and `_iterlines()` is that this
        method is allowed to re-wrap the line if necessary.

        :Parameters:
          width : float or ``None``
            The width of the box in which the text will be fitted. Lines will
            be wrapped if they are wider than this width.
        """
        ctx = self.context
        for line in self._text.split("\n"):
            xb, _, line_width, _, _, _ = ctx.text_extents(line)
            if line_width <= width:
                yield (line, line_width, xb)
                continue

            # We have to wrap the line
            current_line, current_width, last_sep_width = [], 0, 0
            for match in re.finditer(r"(\S+)(\s+)?", line):
                word, sep = match.groups()
                word_width = ctx.text_extents(word)[4]
                if sep:
                    sep_width = ctx.text_extents(sep)[4]
                else:
                    sep_width = 0
                current_width += word_width
                if current_width >= width and current_line:
                    yield ("".join(current_line), current_width - word_width, 0)
                    # Starting a new line
                    current_line, current_width = [word], word_width
                    if sep is not None:
                        current_line.append(sep)
                else:
                    current_width += last_sep_width
                    current_line.append(word)
                    if sep is not None:
                        current_line.append(sep)
                last_sep_width = sep_width
            if current_line:
                yield ("".join(current_line), current_width, 0) 

    @property
    def text(self):
        """Returns the text to be drawn."""
        return self._text

    @text.setter
    def text(self, text):
        """Sets the text that will be drawn.
        
        If `text` is ``None``, it will be mapped to an empty string; otherwise,
        it will be converted to a string."""
        if text is None:
            self._text = ""
        else:
            self._text = str(text)

    def text_extents(self):
        """Returns the X-bearing, Y-bearing, width, height, X-advance and
        Y-advance of the text.
        
        For multi-line text, the X-bearing and Y-bearing correspond to the
        first line, while the X-advance is extracted from the last line.
        and the Y-advance is the sum of all the Y-advances. The width and
        height correspond to the entire bounding box of the text."""
        lines = self.text.split("\n")
        if len(lines) <= 1:
            return self.context.text_extents(self.text)

        x_bearing, y_bearing, width, height, x_advance, y_advance = \
            self.context.text_extents(lines[0])

        line_height = self.context.font_extents()[2]
        for line in lines[1:]:
            _, _, w, _, x_advance, ya = self.context.text_extents(line)
            width = max(width, w)
            height += line_height
            y_advance += ya

        return x_bearing, y_bearing, width, height, x_advance, y_advance

def test():
    """Testing routine for L{TextDrawer}"""
    import cairo
    import math

    text = "The quick brown fox\njumps over a\nlazy dog"
    width, height = (600, 1000)

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    context = cairo.Context(surface)
    drawer = TextDrawer(context, text)

    context.set_source_rgb(1, 1, 1)
    context.set_font_size(16.)
    context.rectangle(0, 0, width, height)
    context.fill()

    context.set_source_rgb(0.5, 0.5, 0.5)
    for i in range(200, width, 200):
        context.move_to(i, 0)
        context.line_to(i, height)
        context.stroke()
    for i in range(200, height, 200):
        context.move_to(0, i)
        context.line_to(width, i)
        context.stroke()
    context.set_source_rgb(0.75, 0.75, 0.75)
    context.set_line_width(0.5)
    for i in range(100, width, 200):
        context.move_to(i, 0)
        context.line_to(i, height)
        context.stroke()
    for i in range(100, height, 200):
        context.move_to(0, i)
        context.line_to(width, i)
        context.stroke()

    def mark_point(red, green, blue):
        """Marks the current point on the canvas by the given color"""
        x, y = context.get_current_point()
        context.set_source_rgba(red, green, blue, 0.5)
        context.arc(x, y, 4, 0, 2 * math.pi)
        context.fill()

    # Testing drawer.draw_at()
    for i, halign in enumerate(("left", "center", "right")):
        # Mark the reference points
        context.move_to(i * 200, 40)
        mark_point(0, 0, 1)
        context.move_to(i * 200, 140)
        mark_point(0, 0, 1)

        # Draw the text
        context.set_source_rgb(0, 0, 0)
        drawer.halign = halign
        drawer.draw_at(i * 200, 40)
        drawer.draw_at(i * 200, 140, width=200)

        # Mark the new reference point
        mark_point(1, 0, 0)

    # Testing TextDrawer.draw()
    for i, halign in enumerate(("left", "center", "right")):
        for j, valign in enumerate(("top", "center", "bottom")):
            # Draw the text
            context.set_source_rgb(0, 0, 0)
            drawer.halign = halign
            drawer.valign = valign
            drawer.bbox = (i*200, j*200+200, i*200+200, j*200+400)
            drawer.draw()
            # Mark the new reference point
            mark_point(1, 0, 0)

    # Testing TextDrawer.wrap()
    drawer.text = "Jackdaws love my big sphinx of quartz. Yay, wrapping! " + \
                  "Jackdaws love my big sphinx of quartz.\n\n" + \
                  "Jackdaws  love  my  big  sphinx  of  quartz."
    drawer.valign = TextDrawer.TOP
    for i, halign in enumerate(("left", "center", "right")):
        context.move_to(i * 200, 840)

        # Mark the reference point
        mark_point(0, 0, 1)

        # Draw the text
        context.set_source_rgb(0, 0, 0)
        drawer.halign = halign
        drawer.draw_at(i * 200, 840, width=199, wrap=True)

        # Mark the new reference point
        mark_point(1, 0, 0)

    surface.write_to_png("test.png")

if __name__ == "__main__":
    test()