Providing file meta-data support with KFile plugins | ||
|---|---|---|
| <<< Previous | Next >>> | |
In the previous section, we built
a trivial KFile plugin for the MNG file
format. This section moves on
to actually getting real data from the file, and inserting it into
relevant groups and items.
Before we start on a real file, it might be worth reviewing how we
actually add additional items and additional groups to a
KFile plugin.
As in the trivial example, we added individual items to the
"mngInfo" group with the
addItemInfo function in the class constructor,
and the addInfo function in the
readInfo method.
As an example, lets look at adding another integer item (reprenting a frame rate) and some text to the trivial example from the previous section.
Example 5. source file example, two extra items
1 /***************************************************************************
2 * Copyright (C) 2004 by Brad Hards *
3 * bhards@bigpond.net.au *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include <config.h>
21 #include "kfile_mng.h"
22 #include <kgenericfactory.h>
23 typedef KGenericFactory<mngPlugin> mngFactory;
24 K_EXPORT_COMPONENT_FACTORY(kfile_mng, mngFactory( "kfile_mng" ))
25 mngPlugin::mngPlugin(QObject *parent, const char *name,
26 const QStringList &args)
27 : KFilePlugin(parent, name, args)
28 {
29 KFileMimeTypeInfo* info = addMimeTypeInfo( "video/x-mng" );
30 // our new group
31 KFileMimeTypeInfo::GroupInfo* group = 0L;
32 group = addGroupInfo(info, "mngInfo", i18n("MNG Information"));
33 KFileMimeTypeInfo::ItemInfo* item;
34 // our new items in the group
35 item = addItemInfo(group, "Items", i18n("Items"), QVariant::Int);
36 item = addItemInfo(group, "Size", i18n("Size"), QVariant::Int);
37 setUnit(item, KFileMimeTypeInfo::KiloBytes);
38 addItemInfo(group, "framerate", i18n("Frame Rate"), QVariant::Int);
39 addItemInfo(group, "Text", i18n("Document type"), QVariant::String);
40 }
41 bool mngPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
42 {
43 KFileMetaInfoGroup group = appendGroup(info, "mngInfo");
44 appendItem(group, "Items", 100);
45 appendItem(group, "Size", int(5000/1024));
46 appendItem(group, "framerate", 40);
47 appendItem(group, "Text", "Anonymous MNG file");
48
49 return true;
50 }
51 #include "kfile_mng.moc"
|
All that has changed is the additional addItemInfo function calls in lines 38 and 39, and the additional appendItem calls in lines 46 and 47. Note that in the calls to addItemInfo, we ignore the return value - we don't need a reference to add any units or the like, so it isn't important.
When built and installed, this updated KFile
plugin produces meta-data as shown below:
Note that the framerate label and
Text label are never displayed (although
Frame Rate and Document Type
labels are displayed). The second argument to
addItemInfo and appendItem
is only used to associate the text label (provided in the third
argument to addItemInfo) and the value for that
label (provided in the third argument to
appendItem.
Adding additional groups is much like adding additional items, except the relevant function calls are addGroupInfo in the constructor, and appendGroup in the readInfo method.
In this example, we'll take the items in our plugin, and divide them into various groups.
Example 6. source file example, with extra groups and items
1 /***************************************************************************
2 * Copyright (C) 2004 by Brad Hards *
3 * bhards@bigpond.net.au *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include <config.h>
21 #include "kfile_mng.h"
22 #include <kgenericfactory.h>
23 typedef KGenericFactory<mngPlugin> mngFactory;
24 K_EXPORT_COMPONENT_FACTORY(kfile_mng, mngFactory( "kfile_mng" ))
25 mngPlugin::mngPlugin(QObject *parent, const char *name,
26 const QStringList &args)
27 : KFilePlugin(parent, name, args)
28 {
29 KFileMimeTypeInfo* info = addMimeTypeInfo( "video/x-mng" );
30 // our three groups
31 KFileMimeTypeInfo::GroupInfo* titleGroup = 0L;
32 titleGroup = addGroupInfo(info, "mngTitle", i18n("MNG Title"));
33 KFileMimeTypeInfo::GroupInfo* featuresGroup = 0L;
34 featuresGroup = addGroupInfo(info, "mngFeatures", i18n("MNG Features"));
35 KFileMimeTypeInfo::GroupInfo* sizeGroup = 0L;
36 sizeGroup = addGroupInfo(info, "mngSize", i18n("MNG Size"));
37 KFileMimeTypeInfo::ItemInfo* item;
38 // our new items in the group
39 addItemInfo(featuresGroup, "Items", i18n("Items"), QVariant::Int);
40 item = addItemInfo(sizeGroup, "Size", i18n("Size"), QVariant::Int);
41 setUnit(item, KFileMimeTypeInfo::KiloBytes);
42 addItemInfo(featuresGroup, "framerate", i18n("Frame Rate"), QVariant::Int);
43 addItemInfo(titleGroup, "Text", i18n("Document type"), QVariant::String);
44 addItemInfo(featuresGroup, "Text", i18n("Text lines included"), QVariant::Int);
45 }
46 bool mngPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
47 {
48 KFileMetaInfoGroup featuresGroup = appendGroup(info, "mngFeatures");
49 KFileMetaInfoGroup titleGroup = appendGroup(info, "mngTitle");
50 KFileMetaInfoGroup sizeGroup = appendGroup(info, "mngSize");
51 appendItem(featuresGroup, "Items", 100);
52 appendItem(sizeGroup, "Size", int(5000/1024));
53 appendItem(featuresGroup, "Text", 3);
54 appendItem(featuresGroup, "framerate", 40);
55 appendItem(titleGroup, "Text", "Anonymous MNG file");
56
57 return true;
58 }
59 #include "kfile_mng.moc"
|
In lines 31 to 36, we create the three groups using addGroupInfo. In line 39, we add an item to the features group, while lines 40 and 41 add an item to the size group. Lines 42 and 44 add more items to the features group, while line 43 adds an item to the title group.
Lines 48 to 50 add the various labels to the display, while lines 51 to 55 add the various items to their groups.
There are a couple of things to bring out from this. Note that the items can be added to groups in any order, and groups can be created in any order (although you do have to create the group before you create the item). Also, the same label can be the same for several items, as long as they are assigned to different groups.
When built and installed, this updated KFile
plugin produces meta-data as shown below:
Now that we understand how to add items and groups, we can move on to using real data instead of the fixed numbers used in the trivial examples.
There are two common approaches for getting the meta-data from a file, irrespective of the actual data that the file contains. The first is to use some form of library (with C or C++ bindings) and obtain the data by function calls. The second approach is to parse the library with standard C++ I/O functions. Each approach has its place, so I'll actually do both, pointing out the advantages of each approach.
The advantage to using a helper library (in this case, libmng) is that it provides an abstraction from the raw file format, which usually makes for easier programming, especially on more complex file formats, or those with numerous variations and assorted bugs.
Using a helper library often also means that when the file format changes, the library interface can remain the same, and the library authors deal with the code changes necessary to handle the underlying format change - your plugin may only need to be relinked to handle the changes. With luck, you may be able to just upgrade the underlying shared libraries and not need to change your plugin at all.
Instead of first trying to get libmng working inside the plugin, I adapted one of the examples provided with libmng to do a dump of the meta-data. When I was confident that I had the rough concept worked out, I moved onto integration of the libmng support into the plugin.
The dependency on libmng means that we
should only try to build the plugin if
libmng is available, and works. The
standard technique for handling this case is an
autoconf check. In the integrated build
case (that is, part of KDE), we add a file
configure.in.in in a convenient place (typically
the directory containing the rest of the KFile
plugin source), and use the
results of the configure check in the next highest level
directory.
The configuration check that I came up with looks like the following.
Example 7. configuration check example
1 AC_DEFUN([AC_FIND_MNG],
2 [
3 AC_REQUIRE([KDE_CHECK_EXTRA_LIBS])
4 AC_REQUIRE([AC_FIND_ZLIB])
5 AC_MSG_CHECKING([for libmng])
6 AC_CACHE_VAL(ac_cv_lib_mng,
7 [
8 kde_save_LIBS="$LIBS"
9 if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then
10 LIBS="$LIBS $all_libraries $USER_LDFLAGS -lmng $LIBZ -lm -lX11 $LIBSOCKET"
11 else
12 LIBS="$LIBS $all_libraries $USER_LDFLAGS -lmng $LIBZ -lm"
13 fi
14 kde_save_CFLAGS="$CFLAGS"
15 CFLAGS="$CFLAGS $all_includes $USER_INCLUDES"
16 AC_TRY_LINK(dnl
17 [
18 #include<libmng.h>
19 ],
20 [
21 mng_version_text();
22 ],
23 eval "ac_cv_lib_mng='-lmng $LIBZ -lm'",
24 eval "ac_cv_lib_mng=no"
25 )
26 LIBS="$kde_save_LIBS"
27 CFLAGS="$kde_save_CFLAGS"
28 ])dnl
29 if eval "test ! \"`echo $ac_cv_lib_mng`\" = no"; then
30 AC_DEFINE_UNQUOTED(HAVE_LIBMNG, 1, [Define if you have libmng])
31 LIB_MNG="$ac_cv_lib_mng"
32 AC_SUBST(LIB_MNG)
33 AC_MSG_RESULT($ac_cv_lib_mng)
34 else
35 AC_MSG_RESULT(no)
36 LIB_MNG=""
37 AC_SUBST(LIB_MNG)
38 fi
39 ])
40 AC_FIND_MNG
41 AM_CONDITIONAL(include_MNG_MODULES, test -n "$LIB_MNG")
|
If you haven't seen configure checks like this before, then it might be worth reviewing the manual and info pages for autoconf. For now, it is enough to know that they are written in a macro substitution language known as M4.
I'll work through this quickly. Lines 1 through 39 define the check we will use. Lines 3 and 4 establish some prequisites which will be run if they haven't already been done. Line 5 just outputs a progress message for when ./configure is run. Lines 6 to 28 are the main check, and for potential future efficiency, we cache the results. Lines 8 to 13 establish a suitable set of libraries to test, while line 26 restores the previous value. Lines 14 and 15 establish a suitable set of header file locations, and again we are restoring the previous value at line 27. Lines 16 to 25 tests if a simple file relying on libmng will compile and link. Lines 29 to 38 substitute some appropriate variables depending on whether the compile and link test passed. Line 40 actually invokes the test we just defined, and line 41 sets up an appropriate substitution for automake depending on the results of the test.
As mentioned above, we handle the presence or absence of support
libraries by deciding whether to compile a particular directory or
not. For example, for code that is in
kdegraphics/kfile-plugins/mng/, we use the
results of the ./configure check in
kdegraphics/kfile-plugins/Makefile.am to
determine whether to compile any code in the mng
directory. There are other ways to handle it, but this way is common
for KFile plugins across KDE, and handles most
situations.
The actual change to kdegraphics/kfile-plugins/Makefile.am can be seen in the following file.
Example 8. configuration check Makefile.am example
1 if include_EXR_MODULES
2 KFILE_EXR_SUBDIR=exr
3 endif
4 if include_TIFF
5 KFILE_TIFF_SUBDIR=tiff
6 endif
7 if include_MNG_MODULES
8 KFILE_MNG_SUBDIR=mng
9 endif
10 SUBDIRS=dvi pdf png ps jpeg xbm bmp tga ico pcx $(KFILE_TIFF_SUBDIR) pnm \
11 $(KFILE_EXR_SUBDIR) $(KFILE_MNG_SUBDIR)
|
In this example file, I've added lines 7 to 9, and the matching entry on the end of the last line. Basically if the configure check returns an appropriate value, then we add the mng directory to the list of subdirectories to compile.
So now we have an appropriate development environment, we can move onto the actual use of libmng. The main code now looks like the following:
Example 9. source file example, using libmng
1 /***************************************************************************
2 * Copyright (C) 2004 by Brad Hards *
3 * bhards@bigpond.net.au *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include <config.h>
21 #include <libmng.h>
22 #include "kfile_mng.h"
23 #include <kgenericfactory.h>
24 typedef KGenericFactory<mngPlugin> mngFactory;
25 K_EXPORT_COMPONENT_FACTORY(kfile_mng, mngFactory( "kfile_mng" ))
26 mngPlugin::mngPlugin(QObject *parent, const char *name,
27 const QStringList &args)
28 : KFilePlugin(parent, name, args)
29 {
30 KFileMimeTypeInfo* info = addMimeTypeInfo( "video/x-mng" );
31 KFileMimeTypeInfo::GroupInfo* technicalGroup = 0L;
32 technicalGroup = addGroupInfo(info, "mngTechnical", i18n("Technical"));
33 KFileMimeTypeInfo::ItemInfo* item;
34 // our new items in the group
35 addItemInfo(technicalGroup, "ImageType", i18n("Image Type"), QVariant::Int);
36 addItemInfo(technicalGroup, "ImageWidth", i18n("Image Width"), QVariant::Int);
37 addItemInfo(technicalGroup, "ImageHeight", i18n("Image Height"), QVariant::Int);
38 addItemInfo(technicalGroup, "Ticks", i18n("Ticks"), QVariant::Int);
39 addItemInfo(technicalGroup, "Frame Count", i18n("Frame Count"), QVariant::Int);
40 addItemInfo(technicalGroup, "Layer Count", i18n("Layer Count"), QVariant::Int);
41 item = addItemInfo(technicalGroup, "PlayTime", i18n("Play Time"), QVariant::Int);
42 setUnit(item, KFileMimeTypeInfo::Seconds);
43 addItemInfo(technicalGroup, "Simplicity", i18n("Simplicity"), QVariant::Int);
44 }
45 typedef struct user_struct {
46 FILE *hFile; /* file handle */
47 } userdata;
48 typedef userdata * userdatap;
49 mng_ptr myalloc (mng_size_t iSize)
50 {
51 return (mng_ptr)calloc (1, (size_t)iSize);
52 }
53 void myfree (mng_ptr pPtr, mng_size_t /*iSize*/)
54 {
55 free (pPtr);
56 }
57 mng_bool myopenstream (mng_handle /*hMNG*/)
58 {
59 return MNG_TRUE;
60 }
61 mng_bool myclosestream (mng_handle /*hMNG*/)
62 {
63 return MNG_TRUE;
64 }
65 mng_bool myreaddata (mng_handle hMNG,
66 mng_ptr pBuf,
67 mng_uint32 iSize,
68 mng_uint32 *iRead)
69 {
70 userdatap pMydata = (userdatap)mng_get_userdata (hMNG);
71 *iRead = fread (pBuf, 1, iSize, pMydata->hFile);
72 return MNG_TRUE;
73 }
74 bool mngPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
75 {
76 userdatap pMydata;
77 mng_handle hMNG;
78 mng_retcode iRC;
79 pMydata = (userdatap)calloc (1, sizeof (userdata));
80 if (pMydata == NULL) {
81 kdWarning() << "Cannot allocate a data buffer" << endl;
82 return false;
83 }
84 /* can we open the file ? */
85 if ((pMydata->hFile = fopen (info.path().ascii(), "rb")) == NULL) {
86 /* error out if we can't */
87 kdWarning() << "Cannot open input file: " << info.path().ascii() << endl;
88 return false;
89 }
90 /* let's initialize the library */
91 hMNG = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL);
92 if (!hMNG) {
93 kdWarning() << "Cannot initialize libmng" << endl;
94 return false;
95 } else { /* setup callbacks */
96 if ( ((iRC = mng_setcb_openstream (hMNG, myopenstream )) != 0) ||
97 ((iRC = mng_setcb_closestream (hMNG, myclosestream)) != 0) ||
98 ((iRC = mng_setcb_readdata (hMNG, myreaddata )) != 0) )
99 kdWarning() << "Cannot set callbacks for libmng" << endl;
100 else { /* read the file into memory */
101 if ((iRC = mng_read (hMNG)) != 0)
102 kdWarning() << "Cannot read the file" << endl;
103 else {
104 KFileMetaInfoGroup technicalGroup = appendGroup(info, "mngTechnical");
105 appendItem(technicalGroup, "ImageType", mng_get_imagetype(hMNG));
106 appendItem(technicalGroup, "ImageWidth", mng_get_imagewidth(hMNG));
107 appendItem(technicalGroup, "ImageHeight", mng_get_imageheight(hMNG));
108 appendItem(technicalGroup, "Ticks", mng_get_ticks(hMNG));
109 appendItem(technicalGroup, "Frame Count", mng_get_framecount(hMNG));
110 appendItem(technicalGroup, "Layer Count", mng_get_layercount(hMNG));
111 appendItem(technicalGroup, "Play time", mng_get_playtime(hMNG));
112 appendItem(technicalGroup, "Simplicity", mng_get_simplicity(hMNG));
113 }
114 }
115 mng_cleanup (&hMNG); /* cleanup the library */
116 }
117 fclose (pMydata->hFile); /* cleanup */
118 free (pMydata);
119 return true;
120 }
121 #include "kfile_mng.moc"
|
Stepping through the changes since the previous version, we can see
that the items have changed (lines 34 to 43) to match what
libmng can provide. Lines 45 to 73 establish
callback functions, required for use of
libmng. Lines 76 to 83 set up some
variables and structures. Lines 85 to 89 handle the opening of the
file, which is passed in as part of the
KFileMetaInfo argument. Line 91 sets up the
MNG library, and lines 92 to 94 handle the failure of that
setup. Lines 96 to 98 set the callback functions, and line 99 handles
any failures in setting the callbacks. Line 101 reads in the
header. Lines 105 to 112 use various libmng
functions to actually add the entries.
So now we've seen how to make a plugin that can actually do something. Using this code with Konqueror produces a result like the screenshot below.
Instead of using libmng, we could write code to read the values from the file directly. The advantage to parsing the file directly is that it provides finer grained control over the parsing, and avoids a dependency on any external libraries. It can also be used to avoid having to call out to a C-style API, with consequential risks for string handling bugs and the like. Direct parsing is also obviously applicable where the available libraries do not provide a convenient function, or where no libraries are available.
Since there is no need for libmng, we don't need any checks in configure.in.in and the file doesn't need to exist. The build is now also not conditional, so we just add the directory name to the next higher level directory.
TO BE COMPLETED.
In previous examples, we've seen items added that have particular semantics associated - for example, the size item in the trivial example had a unit of kilobytes associated with it. There are is a range of particular additional information that can be associated with an item. The main type of information is the units that are associated with an item, although there is also the concept of hints that are used by certain applications.
The available units are:
SecondThis item represents a time in seconds
MillisecondsThis item represents a time in thousandths of a second
BitsPerSecondThis item represents a bit rate in bits per second
PixelsThis item represents a number of pixels
PixelsThis item represents a size in pixels
InchesThis item represents a size in inches
CentimetersThis item represents a size in centimetres
MillimetersThis item represents a size in millimetres
BytesThis item represents a size in bytes
KiloBytesThis item represents a size in kilobytes (1024 bytes)
FramesPerSecondThis item represents a rate in frames per second
HertzThis item represents a rate in Hertz (1/seconds)
DotsPerInchThis item represents a resolution in dots per inch
BitsPerPixelThis item represents a pixel depth in bits per pixel
The available hints are:
NameThis item is the name or title of the document
AuthorThis item is the name of the person or organisation that created the content of the file
DescriptionThis item is a text description of the content of the file
WidthThis item is a width in pixels. This only has meaning for graphics.
HeightThis item is a height in pixels. This only has meaning for graphics.
SizeThis item is a width in pixels and a height in pixels. This only has meaning for graphics.
BitrateThis item is the bitrate for the content. This only has meaning for media files.
HiddenThis item should not normally be shown to the user.
ThumbnailThis item is a thumbnail image of the content of the file. This only has meaning for graphics files.
| <<< Previous | Home | Next >>> |
| Getting started - MNG file | Preferred Groups and Items |