Exemplo n.º 1
0
    def render(self, cairo_surface, dpi, osm_date):
        ctx = cairo.Context(cairo_surface)

        self._render_front_page(ctx, cairo_surface, dpi, osm_date)
        self._render_blank_page(ctx, cairo_surface, dpi)

        ctx.save()

        # Prepare to draw the map at the right location
        ctx.translate(
                commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT),
                commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT))

        self._render_overview_page(ctx, cairo_surface, dpi)

        for map_number, (canvas, grid) in enumerate(self.pages):

            rendered_map = canvas.get_rendered_map()
            LOG.debug('Mapnik scale: 1/%f' % rendered_map.scale_denominator())
            LOG.debug('Actual scale: 1/%f' % canvas.get_actual_scale())
            mapnik.render(rendered_map, ctx)

            # Place the vertical and horizontal square labels
            ctx.save()
            ctx.translate(commons.convert_pt_to_dots(self.grayed_margin_pt),
                      commons.convert_pt_to_dots(self.grayed_margin_pt))
            self._draw_labels(ctx, grid,
                  commons.convert_pt_to_dots(self._usable_area_width_pt) \
                        - 2 * commons.convert_pt_to_dots(self.grayed_margin_pt),
                  commons.convert_pt_to_dots(self._usable_area_height_pt) \
                        - 2 * commons.convert_pt_to_dots(self.grayed_margin_pt),
                  commons.convert_pt_to_dots(self._grid_legend_margin_pt))

            ctx.restore()

            # Render the page number
            draw_utils.render_page_number(ctx, map_number+4,
                                          self._usable_area_width_pt,
                                          self._usable_area_height_pt,
                                          self.grayed_margin_pt,
                                          transparent_background = True)
            self._render_neighbour_arrows(ctx, cairo_surface, map_number,
                                          len(unicode(len(self.pages)+4)))

            cairo_surface.show_page()
        ctx.restore()

        mpsir = MultiPageStreetIndexRenderer(self.rc.i18n,
                                             ctx, cairo_surface,
                                             self.index_categories,
                                             (Renderer.PRINT_SAFE_MARGIN_PT,
                                              Renderer.PRINT_SAFE_MARGIN_PT,
                                              self._usable_area_width_pt,
                                              self._usable_area_height_pt),
                                              map_number+5)

        mpsir.render()

        cairo_surface.flush()
Exemplo n.º 2
0
    def render(self, cairo_surface, dpi, osm_date):
        ctx = cairo.Context(cairo_surface)

        self._render_front_page(ctx, cairo_surface, dpi, osm_date)
        self._render_blank_page(ctx, cairo_surface, dpi)

        ctx.save()

        # Prepare to draw the map at the right location
        ctx.translate(
            commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT),
            commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT))

        self._render_overview_page(ctx, cairo_surface, dpi)

        for map_number, (canvas, grid) in enumerate(self.pages):

            rendered_map = canvas.get_rendered_map()
            LOG.debug('Mapnik scale: 1/%f' % rendered_map.scale_denominator())
            LOG.debug('Actual scale: 1/%f' % canvas.get_actual_scale())
            mapnik.render(rendered_map, ctx)

            # Place the vertical and horizontal square labels
            ctx.save()
            ctx.translate(commons.convert_pt_to_dots(self.grayed_margin_pt),
                          commons.convert_pt_to_dots(self.grayed_margin_pt))
            self._draw_labels(ctx, grid,
                  commons.convert_pt_to_dots(self._usable_area_width_pt) \
                        - 2 * commons.convert_pt_to_dots(self.grayed_margin_pt),
                  commons.convert_pt_to_dots(self._usable_area_height_pt) \
                        - 2 * commons.convert_pt_to_dots(self.grayed_margin_pt),
                  commons.convert_pt_to_dots(self._grid_legend_margin_pt))

            ctx.restore()

            # Render the page number
            draw_utils.render_page_number(ctx,
                                          map_number + 4,
                                          self._usable_area_width_pt,
                                          self._usable_area_height_pt,
                                          self.grayed_margin_pt,
                                          transparent_background=True)
            self._render_neighbour_arrows(ctx, cairo_surface, map_number,
                                          len(unicode(len(self.pages) + 4)))

            cairo_surface.show_page()
        ctx.restore()

        mpsir = MultiPageStreetIndexRenderer(
            self.rc.i18n, ctx, cairo_surface, self.index_categories,
            (Renderer.PRINT_SAFE_MARGIN_PT, Renderer.PRINT_SAFE_MARGIN_PT,
             self._usable_area_width_pt, self._usable_area_height_pt),
            map_number + 5)

        mpsir.render()

        cairo_surface.flush()
Exemplo n.º 3
0
    def _render_overview_page(self, ctx, cairo_surface, dpi):
        rendered_map = self.overview_canvas.get_rendered_map()
        mapnik.render(rendered_map, ctx)

        # draw pages numbers
        self._draw_overview_labels(ctx, self.overview_canvas, self.overview_grid,
              commons.convert_pt_to_dots(self._usable_area_width_pt),
              commons.convert_pt_to_dots(self._usable_area_height_pt))
        # Render the page number
        draw_utils.render_page_number(ctx, 3,
                                      self._usable_area_width_pt,
                                      self._usable_area_height_pt,
                                      self.grayed_margin_pt,
                                      transparent_background = True)

        cairo_surface.show_page()
Exemplo n.º 4
0
    def _render_overview_page(self, ctx, cairo_surface, dpi):
        rendered_map = self.overview_canvas.get_rendered_map()
        mapnik.render(rendered_map, ctx)

        # draw pages numbers
        self._draw_overview_labels(
            ctx, self.overview_canvas, self.overview_grid,
            commons.convert_pt_to_dots(self._usable_area_width_pt),
            commons.convert_pt_to_dots(self._usable_area_height_pt))
        # Render the page number
        draw_utils.render_page_number(ctx,
                                      3,
                                      self._usable_area_width_pt,
                                      self._usable_area_height_pt,
                                      self.grayed_margin_pt,
                                      transparent_background=True)

        cairo_surface.show_page()
Exemplo n.º 5
0
    def _render_blank_page(self, ctx, cairo_surface, dpi):
        """
        Render a blank page with a nice "intentionally blank" notice
        """
        ctx.save()
        ctx.translate(
                commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT),
                commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT))

        # footer notice
        w = self._usable_area_width_pt
        h = self._usable_area_height_pt
        ctx.set_source_rgb(.6,.6,.6)
        draw_utils.draw_simpletext_center(ctx, _('This page is intentionally left '\
                                            'blank.'), w/2.0, 0.95*h)
        draw_utils.render_page_number(ctx, 2,
                                      self._usable_area_width_pt,
                                      self._usable_area_height_pt,
                                      self.grayed_margin_pt,
                                      transparent_background=False)
        cairo_surface.show_page()
        ctx.restore()
Exemplo n.º 6
0
    def _render_blank_page(self, ctx, cairo_surface, dpi):
        """
        Render a blank page with a nice "intentionally blank" notice
        """
        ctx.save()
        ctx.translate(
            commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT),
            commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT))

        # footer notice
        w = self._usable_area_width_pt
        h = self._usable_area_height_pt
        ctx.set_source_rgb(.6, .6, .6)
        draw_utils.draw_simpletext_center(ctx, _('This page is intentionally left '\
                                            'blank.'), w/2.0, 0.95*h)
        draw_utils.render_page_number(ctx,
                                      2,
                                      self._usable_area_width_pt,
                                      self._usable_area_height_pt,
                                      self.grayed_margin_pt,
                                      transparent_background=False)
        cairo_surface.show_page()
        ctx.restore()
Exemplo n.º 7
0
    def render(self, cairo_surface, dpi, osm_date):
        """Renders the map, the index and all other visual map features on the
        given Cairo surface.

        Args:
            cairo_surface (Cairo.Surface): the destination Cairo device.
            dpi (int): dots per inch of the device.
        """
        LOG.info(
            "SinglePageRenderer rendering on %dx%dmm paper at %d dpi."
            % (self.rc.paper_width_mm, self.rc.paper_height_mm, dpi)
        )

        # First determine some useful drawing parameters
        safe_margin_dots = commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT, dpi)
        usable_area_width_dots = commons.convert_pt_to_dots(self._usable_area_width_pt, dpi)
        usable_area_height_dots = commons.convert_pt_to_dots(self._usable_area_height_pt, dpi)

        title_margin_dots = commons.convert_pt_to_dots(self._title_margin_pt, dpi)

        copyright_margin_dots = commons.convert_pt_to_dots(self._copyright_margin_pt, dpi)

        map_coords_dots = map(lambda l: commons.convert_pt_to_dots(l, dpi), self._map_coords)

        ctx = cairo.Context(cairo_surface)

        # Set a white background
        ctx.save()
        ctx.set_source_rgb(1, 1, 1)
        ctx.rectangle(
            0,
            0,
            commons.convert_pt_to_dots(self.paper_width_pt, dpi),
            commons.convert_pt_to_dots(self.paper_height_pt, dpi),
        )
        ctx.fill()
        ctx.restore()

        ##
        ## Draw the map, scaled to fit the designated area
        ##
        ctx.save()

        # prevent map background from filling the full canvas
        ctx.rectangle(map_coords_dots[0], map_coords_dots[1], map_coords_dots[2], map_coords_dots[3])
        ctx.clip()

        # Prepare to draw the map at the right location
        ctx.translate(map_coords_dots[0], map_coords_dots[1])

        # Draw the rescaled Map
        ctx.save()
        scale_factor = dpi / 72
        rendered_map = self._map_canvas.get_rendered_map()
        LOG.debug("Map:")
        LOG.debug("Mapnik scale: 1/%f" % rendered_map.scale_denominator())
        LOG.debug("Actual scale: 1/%f" % self._map_canvas.get_actual_scale())
        mapnik.render(rendered_map, ctx, scale_factor, 0, 0)
        ctx.restore()

        # Draw the rescaled Overlay
        if self.rc.overlay:
            LOG.debug("Overlay:")
            ctx.save()
            scale_factor = dpi / 72
            rendered_overlay = self._overlay_canvas.get_rendered_map()
            mapnik.render(rendered_overlay, ctx, scale_factor, 0, 0)
            ctx.restore()

        # Draw a rectangle around the map
        ctx.rectangle(0, 0, map_coords_dots[2], map_coords_dots[3])
        ctx.stroke()

        # Place the vertical and horizontal square labels
        self._draw_labels(
            ctx,
            self.grid,
            map_coords_dots[2],
            map_coords_dots[3],
            commons.convert_pt_to_dots(self._grid_legend_margin_pt, dpi),
        )
        ctx.restore()

        ##
        ## Draw the title
        ##
        ctx.save()
        ctx.translate(safe_margin_dots, safe_margin_dots)
        self._draw_title(ctx, usable_area_width_dots, title_margin_dots, "Georgia Bold")
        ctx.restore()

        ##
        ## Draw the index, when applicable
        ##
        if self._index_renderer and self._index_area:
            ctx.save()

            # NEVER use ctx.scale() here because otherwise pango will
            # choose different dont metrics which may be incompatible
            # with what has been computed by __init__(), which may
            # require more columns than expected !  Instead, we have
            # to trick pangocairo into believing it is rendering to a
            # device with the same default resolution, but with a
            # cairo resolution matching the 'dpi' specified
            # resolution. See
            # index::render::StreetIndexRenederer::render() and
            # comments within.

            self._index_renderer.render(ctx, self._index_area, dpi)

            ctx.restore()

            # Also draw a rectangle
            ctx.save()
            ctx.rectangle(
                commons.convert_pt_to_dots(self._index_area.x, dpi),
                commons.convert_pt_to_dots(self._index_area.y, dpi),
                commons.convert_pt_to_dots(self._index_area.w, dpi),
                commons.convert_pt_to_dots(self._index_area.h, dpi),
            )
            ctx.stroke()
            ctx.restore()

        ##
        ## Draw the copyright notice
        ##
        ctx.save()

        # Move to the right position
        ctx.translate(
            safe_margin_dots,
            (safe_margin_dots + title_margin_dots + usable_area_height_dots + copyright_margin_dots / 4.0),
        )

        # Draw the copyright notice
        self._draw_copyright_notice(ctx, usable_area_width_dots, copyright_margin_dots, osm_date=osm_date)
        ctx.restore()

        # Draw compass rose
        # TODO: proper positioning/scaling, move to abstract renderer
        ctx.save()
        ctx.translate(50, title_margin_dots + 50)
        ctx.scale(0.33, 0.33)
        compass_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", "..", "images", "compass-rose.svg")
        )
        svg = rsvg.Handle(compass_path)
        svg.render_cairo(ctx)
        ctx.restore()

        # TODO: map scale

        cairo_surface.flush()
Exemplo n.º 8
0
    def render(self, cairo_surface, dpi, osm_date):
        """Renders the map, the index and all other visual map features on the
        given Cairo surface.

        Args:
            cairo_surface (Cairo.Surface): the destination Cairo device.
            dpi (int): dots per inch of the device.
        """
        LOG.info('SinglePageRenderer rendering on %dx%dmm paper at %d dpi.' %
                 (self.rc.paper_width_mm, self.rc.paper_height_mm, dpi))

        # First determine some useful drawing parameters
        safe_margin_dots \
            = commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT, dpi)
        usable_area_width_dots \
            = commons.convert_pt_to_dots(self._usable_area_width_pt, dpi)
        usable_area_height_dots \
            = commons.convert_pt_to_dots(self._usable_area_height_pt, dpi)

        title_margin_dots \
            = commons.convert_pt_to_dots(self._title_margin_pt, dpi)

        copyright_margin_dots \
            = commons.convert_pt_to_dots(self._copyright_margin_pt, dpi)

        map_coords_dots = map(lambda l: commons.convert_pt_to_dots(l, dpi),
                              self._map_coords)

        ctx = cairo.Context(cairo_surface)

        # Set a white background
        ctx.save()
        ctx.set_source_rgb(1, 1, 1)
        ctx.rectangle(0, 0, commons.convert_pt_to_dots(self.paper_width_pt, dpi),
                      commons.convert_pt_to_dots(self.paper_height_pt, dpi))
        ctx.fill()
        ctx.restore()

        ##
        ## Draw the index, when applicable
        ##
        if self._index_renderer and self._index_area:
            ctx.save()

            # NEVER use ctx.scale() here because otherwise pango will
            # choose different dont metrics which may be incompatible
            # with what has been computed by __init__(), which may
            # require more columns than expected !  Instead, we have
            # to trick pangocairo into believing it is rendering to a
            # device with the same default resolution, but with a
            # cairo resolution matching the 'dpi' specified
            # resolution. See
            # index::render::StreetIndexRenederer::render() and
            # comments within.

            self._index_renderer.render(ctx, self._index_area, dpi)

            ctx.restore()

            # Also draw a rectangle
            ctx.save()
            ctx.rectangle(commons.convert_pt_to_dots(self._index_area.x, dpi),
                          commons.convert_pt_to_dots(self._index_area.y, dpi),
                          commons.convert_pt_to_dots(self._index_area.w, dpi),
                          commons.convert_pt_to_dots(self._index_area.h, dpi))
            ctx.stroke()
            ctx.restore()


        ##
        ## Draw the map, scaled to fit the designated area
        ##
        ctx.save()

        # Prepare to draw the map at the right location
        ctx.translate(map_coords_dots[0], map_coords_dots[1])

        # Draw the rescaled Map
        ctx.save()
        rendered_map = self._map_canvas.get_rendered_map()
        LOG.debug('Mapnik scale: 1/%f' % rendered_map.scale_denominator())
        LOG.debug('Actual scale: 1/%f' % self._map_canvas.get_actual_scale())
        mapnik.render(rendered_map, ctx)
        ctx.restore()

        # Draw a rectangle around the map
        ctx.rectangle(0, 0, map_coords_dots[2], map_coords_dots[3])
        ctx.stroke()

        # Place the vertical and horizontal square labels
        self._draw_labels(ctx, self.grid,
                          map_coords_dots[2],
                          map_coords_dots[3],
                          commons.convert_pt_to_dots(self._grid_legend_margin_pt,
                                                   dpi))
        ctx.restore()

        ##
        ## Draw the title
        ##
        ctx.save()
        ctx.translate(safe_margin_dots, safe_margin_dots)
        self._draw_title(ctx, usable_area_width_dots,
                         title_margin_dots, 'Georgia Bold')
        ctx.restore()

        ##
        ## Draw the copyright notice
        ##
        ctx.save()

        # Move to the right position
        ctx.translate(safe_margin_dots,
                      ( safe_margin_dots + title_margin_dots
                        + usable_area_height_dots
                        + copyright_margin_dots/4. ) )

        # Draw the copyright notice
        self._draw_copyright_notice(ctx, usable_area_width_dots,
                                    copyright_margin_dots,
                                    osm_date=osm_date)
        ctx.restore()

        # TODO: map scale
        # TODO: compass rose

        cairo_surface.flush()
Exemplo n.º 9
0
    def render(self, cairo_surface, dpi):
        """Renders the map, the index and all other visual map features on the
        given Cairo surface.

        Args:
            cairo_surface (Cairo.Surface): the destination Cairo device.
            dpi (int): dots per inch of the device.
        """
        LOG.info('SinglePageRenderer rendering on %dx%dmm paper at %d dpi.' %
                 (self.rc.paper_width_mm, self.rc.paper_height_mm, dpi))

        # First determine some useful drawing parameters
        safe_margin_dots \
            = commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT, dpi)
        usable_area_width_dots \
            = commons.convert_pt_to_dots(self._usable_area_width_pt, dpi)
        usable_area_height_dots \
            = commons.convert_pt_to_dots(self._usable_area_height_pt, dpi)

        title_margin_dots \
            = commons.convert_pt_to_dots(self._title_margin_pt, dpi)

        copyright_margin_dots \
            = commons.convert_pt_to_dots(self._copyright_margin_pt, dpi)

        map_coords_dots = map(lambda l: commons.convert_pt_to_dots(l, dpi),
                              self._map_coords)

        ctx = cairo.Context(cairo_surface)

        ##
        ## Draw the index, when applicable
        ##
        if self._index_renderer and self._index_area:
            ctx.save()

            # NEVER use ctx.scale() here because otherwise pango will
            # choose different dont metrics which may be incompatible
            # with what has been computed by __init__(), which may
            # require more columns than expected !  Instead, we have
            # to trick pangocairo into believing it is rendering to a
            # device with the same default resolution, but with a
            # cairo resolution matching the 'dpi' specified
            # resolution. See
            # index::render::StreetIndexRenederer::render() and
            # comments within.

            self._index_renderer.render(ctx, self._index_area, dpi)

            ctx.restore()

            # Also draw a rectangle
            ctx.save()
            ctx.rectangle(commons.convert_pt_to_dots(self._index_area.x, dpi),
                          commons.convert_pt_to_dots(self._index_area.y, dpi),
                          commons.convert_pt_to_dots(self._index_area.w, dpi),
                          commons.convert_pt_to_dots(self._index_area.h, dpi))
            ctx.stroke()
            ctx.restore()


        ##
        ## Draw the map, scaled to fit the designated area
        ##
        ctx.save()

        # Prepare to draw the map at the right location
        ctx.translate(map_coords_dots[0], map_coords_dots[1])

        # Draw the rescaled Map
        ctx.save()
        rendered_map = self._map_canvas.get_rendered_map()
        ctx.scale(map_coords_dots[2]
                    / rendered_map.width,
                  map_coords_dots[3]
                    / rendered_map.height)
        mapnik.render(rendered_map, ctx)
        ctx.restore()

        # Draw a rectangle around the map
        ctx.rectangle(0, 0, map_coords_dots[2], map_coords_dots[3])
        ctx.stroke()

        # Place the vertical and horizontal square labels
        self._draw_labels(ctx, self.grid,
                          map_coords_dots[2],
                          map_coords_dots[3],
                          commons.convert_pt_to_dots(self._grid_legend_margin_pt,
                                                   dpi))
        ctx.restore()

        ##
        ## Draw the title
        ##
        ctx.save()
        ctx.translate(safe_margin_dots, safe_margin_dots)
        self._draw_title(ctx, usable_area_width_dots,
                         title_margin_dots, 'Georgia Bold')
        ctx.restore()

        ##
        ## Draw the copyright notice
        ##
        ctx.save()

        # Move to the right position
        ctx.translate(safe_margin_dots,
                      ( safe_margin_dots + title_margin_dots
                        + usable_area_height_dots
                        + copyright_margin_dots/4. ) )

        # Draw the copyright notice
        self._draw_copyright_notice(ctx, usable_area_width_dots,
                                    copyright_margin_dots)
        ctx.restore()

        # TODO: map scale
        # TODO: compass rose

        cairo_surface.flush()
Exemplo n.º 10
0
    def _render_neighbour_arrows(self, ctx, cairo_surface, map_number,
                                 max_digit_number):
        nb_previous_pages = 4
        current_line, current_col = None, None
        for line_nb in xrange(self.nb_pages_height):
            if map_number in self.page_disposition[line_nb]:
                current_line = line_nb
                current_col = self.page_disposition[line_nb].index(map_number)
                break
        if current_line == None:
            # page not referenced
            return

        # north arrow
        for line_nb in reversed(xrange(current_line)):
            if self.page_disposition[line_nb][current_col] != None:
                north_arrow = self.page_disposition[line_nb][current_col]
                ctx.save()
                ctx.translate(
                    self._usable_area_width_pt / 2,
                    commons.convert_pt_to_dots(self.grayed_margin_pt) / 2)
                self._draw_arrow(ctx, cairo_surface,
                                 north_arrow + nb_previous_pages,
                                 max_digit_number)
                ctx.restore()
                break

        # south arrow
        for line_nb in xrange(current_line + 1, self.nb_pages_height):
            if self.page_disposition[line_nb][current_col] != None:
                south_arrow = self.page_disposition[line_nb][current_col]
                ctx.save()
                ctx.translate(self._usable_area_width_pt/2,
                     self._usable_area_height_pt \
                      - commons.convert_pt_to_dots(self.grayed_margin_pt)/2)
                ctx.rotate(math.pi)
                self._draw_arrow(ctx,
                                 cairo_surface,
                                 south_arrow + nb_previous_pages,
                                 max_digit_number,
                                 reverse_text=True)
                ctx.restore()
                break

        # west arrow
        for col_nb in reversed(xrange(0, current_col)):
            if self.page_disposition[current_line][col_nb] != None:
                west_arrow = self.page_disposition[current_line][col_nb]
                ctx.save()
                ctx.translate(
                    commons.convert_pt_to_dots(self.grayed_margin_pt) / 2,
                    self._usable_area_height_pt / 2)
                ctx.rotate(-math.pi / 2)
                self._draw_arrow(ctx, cairo_surface,
                                 west_arrow + nb_previous_pages,
                                 max_digit_number)
                ctx.restore()
                break

        # east arrow
        for col_nb in xrange(current_col + 1, self.nb_pages_width):
            if self.page_disposition[current_line][col_nb] != None:
                east_arrow = self.page_disposition[current_line][col_nb]
                ctx.save()
                ctx.translate(
                    self._usable_area_width_pt \
                     - commons.convert_pt_to_dots(self.grayed_margin_pt)/2,
                    self._usable_area_height_pt/2)
                ctx.rotate(math.pi / 2)
                self._draw_arrow(ctx, cairo_surface,
                                 east_arrow + nb_previous_pages,
                                 max_digit_number)
                ctx.restore()
                break
Exemplo n.º 11
0
    def _render_neighbour_arrows(self, ctx, cairo_surface, map_number,
                                 max_digit_number):
        nb_previous_pages = 4
        current_line, current_col = None, None
        for line_nb in xrange(self.nb_pages_height):
            if map_number in self.page_disposition[line_nb]:
                current_line = line_nb
                current_col = self.page_disposition[line_nb].index(
                                                             map_number)
                break
        if current_line == None:
            # page not referenced
            return

        # north arrow
        for line_nb in reversed(xrange(current_line)):
            if self.page_disposition[line_nb][current_col] != None:
                north_arrow = self.page_disposition[line_nb][current_col]
                ctx.save()
                ctx.translate(self._usable_area_width_pt/2,
                    commons.convert_pt_to_dots(self.grayed_margin_pt)/2)
                self._draw_arrow(ctx, cairo_surface,
                              north_arrow + nb_previous_pages, max_digit_number)
                ctx.restore()
                break

        # south arrow
        for line_nb in xrange(current_line + 1, self.nb_pages_height):
            if self.page_disposition[line_nb][current_col] != None:
                south_arrow = self.page_disposition[line_nb][current_col]
                ctx.save()
                ctx.translate(self._usable_area_width_pt/2,
                     self._usable_area_height_pt \
                      - commons.convert_pt_to_dots(self.grayed_margin_pt)/2)
                ctx.rotate(math.pi)
                self._draw_arrow(ctx, cairo_surface,
                      south_arrow + nb_previous_pages, max_digit_number,
                      reverse_text=True)
                ctx.restore()
                break

        # west arrow
        for col_nb in reversed(xrange(0, current_col)):
            if self.page_disposition[current_line][col_nb] != None:
                west_arrow = self.page_disposition[current_line][col_nb]
                ctx.save()
                ctx.translate(
                    commons.convert_pt_to_dots(self.grayed_margin_pt)/2,
                    self._usable_area_height_pt/2)
                ctx.rotate(-math.pi/2)
                self._draw_arrow(ctx, cairo_surface,
                               west_arrow + nb_previous_pages, max_digit_number)
                ctx.restore()
                break

        # east arrow
        for col_nb in xrange(current_col + 1, self.nb_pages_width):
            if self.page_disposition[current_line][col_nb] != None:
                east_arrow = self.page_disposition[current_line][col_nb]
                ctx.save()
                ctx.translate(
                    self._usable_area_width_pt \
                     - commons.convert_pt_to_dots(self.grayed_margin_pt)/2,
                    self._usable_area_height_pt/2)
                ctx.rotate(math.pi/2)
                self._draw_arrow(ctx, cairo_surface,
                               east_arrow + nb_previous_pages, max_digit_number)
                ctx.restore()
                break