root/trunk/src/DocumentPage.cxx

Revision 277, 7.5 kB (checked in by jordi, 16 months ago)

Added a patch by Igor Vagulin which adds text selection and copy to clipboard features. This fixes bug #14.

Line 
1// ePDFView - A lightweight PDF Viewer.
2// Copyright (C) 2006 Emma's Software.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18#include <config.h>
19#include <string.h>
20#include <gdk/gdk.h>
21#include "epdfview.h"
22
23using namespace ePDFView;
24
25static const gint BYTES_PER_PIXEL = 3;
26
27///
28/// @brief Constructs a new DocumentPage.
29///
30DocumentPage::DocumentPage ()
31{
32    m_Selection = NULL;
33    m_Data = NULL;
34    m_HasSelection = FALSE;
35    m_Height = 0;
36    m_LinkList = NULL;
37    m_Width = 0;
38}
39
40///
41/// @brief Deletes all dynamically allocated memory for DocumentPage.
42///
43DocumentPage::~DocumentPage ()
44{
45    delete[] m_Data;
46    for ( GList *linkItem = g_list_first (m_LinkList) ;
47          NULL != linkItem ;
48          linkItem = g_list_next (linkItem) )
49    {
50        IDocumentLink *link = (IDocumentLink *)linkItem->data;
51        delete link;
52    }
53    g_list_free (m_LinkList);
54
55    if(m_Selection)
56        gdk_region_destroy(m_Selection);
57}
58
59///
60/// @brief Adds a new link to the page.
61///
62/// When rendering the page, the responsible document will extract the
63/// links from the page and call this function with each links it find.
64///
65/// @param link The link to add. The DocumentPage class will delete it.
66///
67void
68DocumentPage::addLink (IDocumentLink *link)
69{
70    m_LinkList = g_list_prepend (m_LinkList, link);
71}
72
73///
74/// @brief Clears the current selection.
75///
76/// This function is called to remove the current selection of a document
77/// page. If the page has no selection this function does nothing.
78///
79void
80DocumentPage::clearSelection ()
81{
82    if ( m_HasSelection )
83    {
84        invertArea (m_SelectionX1, m_SelectionY1, m_SelectionX2, m_SelectionY2);
85        m_HasSelection = FALSE;
86    }
87
88    if(NULL != m_Selection){
89        invertRegion(m_Selection);
90        gdk_region_destroy(m_Selection);
91        m_Selection = NULL;
92    }
93}
94
95///
96/// @brief Gets the page's data.
97///
98/// Gets the actual pixels the page is formed of. The format of the image
99/// is RGB, using 3 bytes per pixel and a total of getWidth() * getHeight()
100/// pixels.
101///
102/// @return The page's pixels array.
103///
104guchar *
105DocumentPage::getData ()
106{
107    g_assert ( NULL != m_Data &&
108              "Tried to retrieve data without creating a new page.");
109
110    return m_Data;
111}
112
113///
114/// @brief Gets the link for a given position.
115///
116/// This function will get the link whose rectangle is under the position
117/// passed as parameter.
118///
119/// @param x The X coordinate of the link's position.
120/// @param y The Y coordinate of the link's position.
121///
122/// @return The link under the position (x, y) or NULL if no link exists.
123///
124IDocumentLink *
125DocumentPage::getLinkAtPosition (gint x, gint y)
126{
127    IDocumentLink *link = NULL;
128
129    for ( GList *linkItem = g_list_first (m_LinkList) ;
130          NULL != linkItem && NULL == link;
131          linkItem = g_list_next (linkItem) )
132    {
133        IDocumentLink *tmpLink = (IDocumentLink *)linkItem->data;
134        if ( tmpLink->positionIsOver (x, y) )
135        {
136            link = tmpLink;
137        }
138    }
139
140    return link;
141}
142
143///
144/// @brief Gets the page's height.
145///
146/// @return The height of the page's image.
147///
148gint
149DocumentPage::getHeight ()
150{
151    return m_Height;
152}
153
154///
155/// @brief Gets the row stride.
156///
157/// The row stride is the bytes between two consecutive rows of the page's
158/// image retrieved by getData(). Currently this value is 3 * getWidth(),
159/// because I use 3 bytes per pixel, but this can change, so better use this
160/// function.
161///
162/// @return The number of bytes between two consecutive rows.
163///
164gint
165DocumentPage::getRowStride ()
166{
167    return BYTES_PER_PIXEL * getWidth ();
168}
169
170///
171/// @brief Gets the page's width.
172///
173/// @return The width of the page's image.
174///
175gint
176DocumentPage::getWidth ()
177{
178    return m_Width;
179}
180
181///
182/// @brief Inverts the colours of a page's area.
183///
184/// This function is used to mark or clear a selection to the document
185/// page. It just inverts the colours of the rectangle area given as parameters.
186///
187/// @param x1 The X coordinate of the area's top-right corner.
188/// @param y1 The Y coordinate of the area's top-right corner.
189/// @param x2 The X coordinate of the area's bottom-left corner.
190/// @param y2 The Y coordinate of the area's bottom-left corner.
191///
192void
193DocumentPage::invertArea (gint x1, gint y1, gint x2, gint y2)
194{
195    gint rowStride = getRowStride ();
196    guchar *data = getData ();
197    for ( gint y = y1 ; y < y2 ; y++ )
198    {
199        for ( gint x = x1 ; x < x2 ; x++ )
200        {
201            gint position = y * rowStride + ( x * BYTES_PER_PIXEL );
202            data[position + 0] = 255 - data[position + 0];
203            data[position + 1] = 255 - data[position + 1];
204            data[position + 2] = 255 - data[position + 2];
205        }
206    }
207}
208
209void
210DocumentPage::invertRegion (GdkRegion* region)
211{
212    int count;
213    GdkRectangle *rectangles;
214    gdk_region_get_rectangles(region, &rectangles, &count);
215    while(count--){
216        GdkRectangle r = rectangles[count];
217        invertArea(r.x, r.y, r.x + r.width, r.y + r.height);
218    }
219    g_free(rectangles);
220}
221
222
223///
224/// @brief Allocates the memory for a new page.
225///
226/// It also stores the @a width and @a height, to be retrieved by
227/// calling getWidth() and getHeight() respectively.
228///
229/// @param width The width of the new page.
230/// @param height The height of the new page.
231///
232/// @return true if the image could be created. False otherwise.
233///
234gboolean
235DocumentPage::newPage (gint width, gint height)
236{
237    g_assert ( 1 <= width && "Tried to create a 0 width page.");
238    g_assert ( 1 <= height && "Tried to create a 0 height page.");
239
240    m_Width = width;
241    m_Height = height;
242
243    gint dataSize = width * height * BYTES_PER_PIXEL;
244    delete[] m_Data;
245    m_Data = new guchar[width * height * BYTES_PER_PIXEL];
246    if ( NULL != m_Data )
247    {
248        memset (m_Data, 0xff, dataSize);
249    }
250
251    return NULL != m_Data;
252}
253
254///
255/// @brief Marks a selection on the page.
256///
257/// This functions selects an area of the page as a selection and
258/// modifies the image to make it visible. For now it does invert the
259/// colours of the selection.
260///
261/// This function also clears any previously set selection, if any.
262///
263/// @param selection The selection to set on the page. The coordinates
264///                  are unscaled.
265/// @param scale The scale to draw the selection.
266///
267void
268DocumentPage::setSelection (DocumentRectangle &selection, gdouble scale)
269{
270    clearSelection ();
271
272    m_SelectionX1 = MAX ((gint)(selection.getX1 () * scale - 0.5), 0);
273    m_SelectionX2 = MAX ((gint)(selection.getX2 () * scale + 0.5), 1);
274    m_SelectionY1 = MAX ((gint)(selection.getY1 () * scale - 0.5), 0);
275    m_SelectionY2 = MAX ((gint)(selection.getY2 () * scale + 0.5), 1);
276
277    invertArea (m_SelectionX1, m_SelectionY1, m_SelectionX2, m_SelectionY2);
278   
279    m_HasSelection = TRUE;
280}
281
282void
283DocumentPage::setSelection (GdkRegion *region)
284{
285    clearSelection ();
286   
287    invertRegion (region);
288   
289    m_Selection = gdk_region_copy(region);
290}
Note: See TracBrowser for help on using the browser.