четверг, 29 июля 2010 г.

TFL examples - mp3

Format mp3;

Metadata: id3v2, id3v1;

if(id3v2)
{
created = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#contentCreated";
subject = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#subject";
title = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#title";
description = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#description";
comment = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#comment";
artist = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#creator";
album = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#musicAlbum";
genre = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#genre";
composer = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#composer";
performer = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#performer";
liricist = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#lyricist";
publisher = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#publisher";
language = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#landuage";
copyright = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#copyright";
trackNumber = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#trackNumber";
duration = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#duration";
bitrate = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#averageBitrate";
samplerate = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#sampleRate";
codec = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#codec";
channels = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#channels";


&type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
&fullname = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#fullname";
&titleProperty = "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#title";
&albumTrackCount = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#albumTrackCount";
&discNumber = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#setNumber";
&discCount = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#setCount";
&musicClass = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#musicPiece";
&audioClass = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio";
&albumClass = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#MusicAlbum";
&contactClass = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#contact";

Uri a, a1, a2, a3, a4, a5;

T: a1 - type - contactClass;
T: a1 - fullname - performer;

T: a2 - type - contactClass;
T: a2 - fullname - publisher;

T: a - titleProperty - album;
T: a - type - albumClass;

T: a3 - type - contactClass;
T: a3 - fullname - liricist;

T: a4 - type - contactClass;
T: a4 - fullname - composer;

T: a5- albumTrackCount - trackNumber;


}

среда, 14 июля 2010 г.

The first results

The goal of project it is a creating of parsers generator for Strigi. Input of generator it is two file. First – describing of metadata format, second – mapping (it is shows how is related tags of metadata and ontology). I separated it for two reasons: 1) one format can has two or more type of metadata (different version of metadata format or new universal formal like xmp) 2) testing grammar really simpler when it is separated.


My first step it is developing of a language for describing metadata format and language for describing the mapping. I created language base on png, exif, id3, vorbis comment and xmp (last 2 a little bit). The language for metadata it is Tag language (TL – short name) and it is contain main item MetadataKey. This is example for png (file pngchunk.txt)
/* key word Binary means that description starts and MetadataKey is defined by offset and size. I planned to expand the language for finding tag by key word and if it necessary it will start by Text key word.


PngChung it name and prefix for future class of extractor */
1 Binary PngChunk;
2

/* ByteOrder и BitOrder it is order a byte and bit. If it is not defined it will LSB.*/
3 ByteOrder=MSB;
4 BitOrder=MSB;
5

/*StartMetadataKey – it is the first key that define type of metadata. Offset for this key from beginning of file. */
6 StartMetadataKey IHDR(offset=12, size=4);
/* Checking value of key. It is required for StartMetadataKey and can to check a string or a hex value.*/
7 checkKey(IHDR)="IHDR";
8

/*MetadataKey –region that contains tag. It can defined by base key (or key word “start”), offset and size. If first parameter is “start” then offset for key from beginning of file. If first parameter is another MetadataKey the offset for key from end base key. */
9 MetadataKey width(IHDR, offset=0, size=4);
/* Creating tag with name “Widht” and with number value that is saved by MetadataKey. Currently I can get only string and int32 types. But I will expand it for double and int64 in future. */
10 SetTag Width(getNumber(width));
11
12 MetadataKey height(width, offset=0, size=4);
13 SetTag Height(getNumber(height));
14
15 MetadataKey ColorType(height, offset=0, size=1);
16 SetTag Color_type(getNumber(ColorType));
17
18 MetadataKey Compression(ColorType, offset=0, size=1);
19 SetTag Compression_method(getNumber(Compression));
20
21 MetadataKey Filter(Compression, offset=0, size=1);
22 SetTag Filter_method(getNumber(Filter));
23
24 MetadataKey Interlace(Filter, offset=0, size=1);
25 SetTag Interlace_method(getNumber(Interlace));


Additional functions:
getNumberByLink(key, offset, size) or getNumberByLink(offset, size) – get number by offset and size. If key is not defined offset from beginning of file.
getStringByLink(key, offset, size), getStringByLink( offset, size), getStringByLink(key, offset), getStringByLink( offset) – get string by offset. If key is not defined offset from beginning of file. If size is not defined it get all until symbol end of string or end of file.
getValueByLink(key, offset, size), getValueByLink(offset, size) – get data without type conversion. 9never used but exist).
All this function it is short variant of MetadataKey + getValue (getString, getNumber)

Functions:
shift(key,n) – moved key on n steps .
getBit(key,n) – get bit number n inside key.
getByte(key, n) – get byte number n inside key.
(You can fine examples of using these functions in files id3tag.txt и exif.txt)

And of course I implemented loop (currently only wile) and if- else

Language for mapping I called – Tiny Format language or shot TFL. For png example (file png_triplex.txt):
/* Format it is key word. After this key word follow prefix for EndAnalyzer class.*/
1 Format Png;
2

/* Metadata – key word defined list of name of formats metadata . Png has only one type of metadata – it PngChunk*/
3 Metadata: PngChunk;
4
5

/* It checking StartMetadataKey. If it is true then triplex creates according to mapping inside this if .*/
6 if(PngChunk)
7 {
/* The name of tag here has to be same name of tag that was been created by setTag function in file pngchunk.txt */
8 With = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width";
9 Height = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height";
10 Color_type= "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth";
11 Compression_method = "http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm";
12 Interlace_method = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode";
13
14 }



Second step it is writing of translator for TFL and TL languages. Each translator contains scanner, parser and generator of c++ code. For developing of scanner and parser I used coco/r generator. It doesn’t have tool for testing and debug grammar (like AntlrWorks for antlr) but it has more clear documentation. The result of working of the TL translator it is class extractor which extracts tag and saves it in vector of Tag.


/*files PngChunkextractor.h и PngChunkextractor.cpp after working TL translator*/
1 #ifndef PngChunkExtractor_H_
2 #define PngChunkExtractor_H_
3 #include "MetadataKey.h"
4 #include "Tag.h"
5 #include "MetadataFunc.h"
6 #include
7 #include
8
9
10 class PngChunkExtractor {
11 public:
12 PngChunkExtractor(std::ifstream _is) {is=_is;}
13 std::vector toExtract();
14 bool toCheck();
15 private:
16 std::ifstream is;
17 };
18 #endif


The result of working of the TFL translator it is class inheritance from StreamEndAnalyzer class.


/* files Pngendanalyzer.h и Pngendanalyzer.cpp after working of TFL translator */
1 #include "Pngendanalyzer.h"
2 #include
3
4
5 void PngEndAnalyzerFactory::registerFields(FieldRegister& reg) {
6 RF["PngChunk_Color_typeField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
7 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
8 RF["PngChunk_Compression_methodField"]=reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
9 addField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
10 RF["PngChunk_HeightField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
11 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
12 RF["PngChunk_Interlace_methodField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
13 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
14 RF["PngChunk_WithField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
15 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
16 }
17
18
19 signed char PngEndAnalyzer::analyze(AnalysisResult& as, InputStream* in) {
20 int flag=-1;
21 PngChunkExtractor PngChunk(InputStream* in);
22 if(PngChunk.toCheck()){
23 std::vector tag_v=PngChunk.toExtract();
24 flag=0;
25 for(int i=0; igetField(name), t.getNumber()); break;
34 case 2: as.addValue(factory->getField(name), t.getString()); break;
35 }
36 name.clear();
37 }
38 return flag;
39 }


Next steps:
1. To rewrite the MetadataKey for working with InputStream (I think I have done it, but didn’t test well)
2. To add opportunity in TL language to get double type.
3. To add in TFL triplex creating.
4. To add the message about declaration error, encoding and another things that I missed.

Link:
http://gitorious.org/strigi/strigi-grammar/trees/master/Translator


вторник, 13 июля 2010 г.

Экватор - Первые итоги

Экватор - Первые итоги


Итак, цель проекта - написать генератор парсеров для Strigi. Т.е. на вход подается описание формата метаданных и описание того как соотносятся теги и свойства онтологий, а на выходе получался бы парсер который бы извлекал теги и создавал бы триплексы. С чего я начала:


1. С чтения документации форматов метаданных (т.е. только ту часть которая описывает метаданные). Пока осилила только png, exif(jpg),  id3(mp3), немного Vorbis comment (flac) и xmp. После этого увлекательного чтения нужно было придумать свой язык для описания метаданных. Вот это как раз было самым сложным этапом работы и прежде чем прейди к какому-то жизнеспособному решению я сделала штуки 3 неудачных попытки. Язык я назвала Tag language или сокращенно TL–оригинально :) И ключевым элементом в нем стал MetadataKey. На примере png (файл pngtag.txt):


  • /* Ключевое слово Binary означает старт + обозначает способ задания MetadataKey по смещения и размеру (планируется что дальше я расширю язык для извлечения тегов по ключевому слову, тогда для такого формата описание будет начинаться с ключевого слова Text). PngChunk – имя нашего описания, а так же префикс для будущего класса.*/
    1 Binary PngChunk;
    2
    /* ByteOrder и BitOrder порядок байтов и битов. Если эти константы не заданы, то по умолчанию они будут равны LSB.*/
    3 ByteOrder=MSB;
    4 BitOrder=MSB;
    5
    /*StartMetadataKey – это самый первый тег который определяет что те самые метаданные. offset для этого ключа задается всегда от начала файла. */
    6 StartMetadataKey IHDR(offset=12, size=4);
    /*Проверка значения ключа. Для StartMetadataKey –обязательна и может проверять значение строки (как указано ниже) или hex.*/
    7 checkKey(IHDR)="IHDR";
    8
    /*MetadataKey –область в которой хранятся нужные нам данные. Задается тремя параметрами:
    другой MetadataKey или ключевой слово start, означающее начало файла. Этот параметр указывает точку относительно которой будет считаться смещение. В случае если указан MetadataKey – это конец ключа
    offset - смещение
    size – размер ключа*/
    9 MetadataKey width(IHDR, offset=0, size=4);
    /*Создаем тег с именем Widht с целочисленным значением, которое храниться в MetadataKey*/
    10 SetTag Width(getNumber(width));
    11
    12 MetadataKey height(width, offset=0, size=4);
    13 SetTag Height(getNumber(height));
    14
    15 MetadataKey ColorType(height, offset=0, size=1);
    16 SetTag Color_type(getNumber(ColorType));
    17
    18 MetadataKey Compression(ColorType, offset=0, size=1);
    19 SetTag Compression_method(getNumber(Compression));
    20
    21 MetadataKey Filter(Compression, offset=0, size=1);
    22 SetTag Filter_method(getNumber(Filter));
    23
    24 MetadataKey Interlace(Filter, offset=0, size=1);
  • 25 SetTag Interlace_method(getNumber(Interlace));



Еще есть функции getNumberByLink(key, offset, size) или getNumberByLink(offset, size) – берет целое число по смещению и размеру. Если key не задан, то смещение считается от начала файла.
getStringByLink(key, offset, size), getStringByLink( offset, size), getStringByLink(key, offset), getStringByLink( offset) – берет строку по смещению. Если key не задан, то считает смещение от начала файла. Если size не задан, то берет все пока не встретит знак конца строки или не дойдет до конца файла.
getValueByLink(key, offset, size), getValueByLink(offset, size) –берет значение без приведения к типу. (пока нигде не используется). Все эти функции короткая запись MetadataKey + getValue

Функция shift(key,n) – сдвигает ключ key на n позиций.
getBit(key,n) – берет n-ый бит в ключе key.
getByte(key, n) – берет n-ый байт в ключе key.
Примеры использования в файлах id3tag.txt и exif.txt

Так же есть цикл (пока только while) и условия(if - else).

Язык для описания соответствия между тегами и онтологией я назвала Tiny Format Language – TFL. Опять же для png (файл png_triplex.txt):


  • /* Format ключевое слово, после которого идет префикс для имени класса - парсера*/
    1 Format Png;
    2
    /* Metadata – ключевое слово, за которым следует список описаний метаданных. У png только один «вид» метаданных, а у jpg например может быть xmp или exif */
    3 Metadata: PngChunk;
    4
    5
    /*Эта проверка означает, что если мы нашли StartMetadataKey и он прошел проверку, то мы сопоставляем тери и свойства в онтологиях.*/
    6 if(PngChunk)
    7 {
    /* Имя тега в этом описании должно соответствовать имени тега заданным при помощи функции setTag в файле pngtag.txt */
    8 With = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width";
    9 Height = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height";
    10 Color_type= "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth";
    11 Compression_method = "http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm";
    12 Interlace_method = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode";
    13
  • 14 }




2. Далее для этих двух языков нужно было написать трансляторы. Каждый транслятор состоит из сканера, парсера и генератора кода. Для создания сканера и парсера я воспользовалась генератором coco/r. Вообще то сначала я планировала работать с antlr, уж очень у них удобная среда разработки и тестирования грамматики AntlrWork. Но отношения с этим инструментом не сложились, вернее не сложились отношения с его документацией, и я решила реализовывать транслятор при помощи coco.


В результате работы TL транслятора я получаю класс extractor, а в результате работы TFL транслятора класс парсер для strigi.

Вот что получается для png:


  • /*файлы PngChunkextractor.h и PngChunkextractor.cpp результат работы TL транслятора*/
    1 #ifndef PngChunkExtractor_H_
    2 #define PngChunkExtractor_H_
    3 #include "MetadataKey.h"
    4 #include "Tag.h"
    5 #include "MetadataFunc.h"
    6 #include
    7 #include
    8
    9
    10 class PngChunkExtractor {
    11 public:
    12 PngChunkExtractor(std::ifstream _is) {is=_is;}
    13 std::vector toExtract();
    14 bool toCheck();
    15 private:
    16 std::ifstream is;
    17 };
    18 #endif

    /* файлы Pngendanalyzer.h и Pngendanalyzer.cpp – результат работы TFL - транслятора */
    1 #include "Pngendanalyzer.h"
    2 #include
    3
    4
    5 void PngEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    6 RF["PngChunk_Color_typeField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
    7 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
    8 RF["PngChunk_Compression_methodField"]=reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
    9 addField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
    10 RF["PngChunk_HeightField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
    11 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
    12 RF["PngChunk_Interlace_methodField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
    13 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
    14 RF["PngChunk_WithField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
    15 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
    16 }
    17
    18
    19 signed char PngEndAnalyzer::analyze(AnalysisResult& as, InputStream* in) {
    20 int flag=-1;
    21 PngChunkExtractor PngChunk(InputStream* in);
    22 if(PngChunk.toCheck()){
    23 std::vector tag_v=PngChunk.toExtract();
    24 flag=0;
    25 for(int i=0; igetField(name), t.getNumber()); break;
    34 case 2: as.addValue(factory->getField(name), t.getString()); break;
    35 }
    36 name.clear();
    37 }
    38 return flag;
  • 39 }




Что дальше:

1. Подправить классы для работы с потоками Strigi (кажется почти доделала)
2. Добавить в TL возможность брать значения типа double
3. Добавить в TFL возможность создавать триплексы
4. Дописать вывод ошибок декларирования переменных, работу с кодировкой и прочие нюансы, которые я упустила.
5. Доделать пример с exif, png, id3, которые бы иллюстрировали все возможности языка.


Ссылка на исходники:
http://gitorious.org/strigi/strigi-grammar/trees/master/Translator

четверг, 1 июля 2010 г.

Translator

Description of Exif

(It haven't been finished yet.)

Binary Exif;

ByteOrder=LSB;
BitOrder=MSB;

StartMetadataKey APP1_Markeer(offset=2, size=2);

checkKey(APP1_Markeer)=0xFFE1;

MetadataKey APP1(start, offset=2, size=10);
MetadataKey IFD_Offset(APP1, offset=4, size=4);
MetadataKey CountOfTag(APP1, offset=getNumber(IFD_Offset), size=2);
MetadataKey FirstTagID(CountOfTag, offset=0, size=2);
TagID=FirstTagID;

count=getNumber(CountOfTag);
while(count>0)
{
MetadataKey Type(TagID, offset=0, size=2);
MetadataKey ValueOffset(TagID, offset=6, size=4);

if(getValue(TagID)==0x010E)
{
if(getValue(Type)==2) {SetTag Image_description(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag Image_description(getStringByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag Image_description(getString(ValueOffset));}
}

if(getValue(TagID)==0x010F)
{
if(getValue(Type)==2) {SetTag Make(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag Make(getStringByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag Make(getString(ValueOffset));}
}

if(getValue(TagID)==0x0110)
{
if(getValue(Type)==2) {SetTag Model(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag Model(getStringByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag Model(getString(ValueOffset));}
}

if(getValue(TagID)==0x0112)
{
if(getValue(Type)==2) {SetTag Orientation(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag Orientation(getNumberByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag Orientation(getNumber(ValueOffset));}
}

if(getValue(TagID)==0x011A)
{
if(getValue(Type)==2) {SetTag XResolution(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag XResolution(getNumberByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag XResolution(getNumber(ValueOffset));}
}

if(getValue(TagID)==0x011B)
{
if(getValue(Type)==2) {SetTag YResolution(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag YResolution(getNumberByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag YResolution(getNumber(ValueOffset));}
}

if(getValue(TagID)==0x0132)
{
if(getValue(Type)==2) {SetTag DateTime(getStringByLink(APP1, offset=getNumber(ValueOffset)));}
if(getValue(Type)==5) {SetTag DateTime(getStringByLink(TagID, offset=getNumber(ValueOffset), size=8));}
if(getValue(Type)==1 || (getValue(Type)==3) || (getValue(Type)==4)) {SetTag DateTime(getString(ValueOffset));}
}

count-=1;
shift(TagID,10);

}

Is generated parser

#include "MetadataKey.h"
#include "Tag.h"
#include "MetadataFunc.h"
#include
#include


int main(){

std::ifstream is;
int DefaultByteOrder=ByteOrderLSB;
int DefaultBitOrder=BitOrderMSB;
MetadataKey APP1;
MetadataKey CountOfTag;
MetadataKey FirstTagID;
MetadataKey IFD_Offset;
MetadataKey TagID;
MetadataKey Type;
MetadataKey ValueOffset;
int count;


std::vector TagArray;
is.open ("test.mp3", std::ifstream::in | std::ifstream::binary);
if (is.good()) {
MetadataKey APP1_Markeer(is);
APP1_Markeer.setOffset(2);
APP1_Markeer.setSize(2);
if(!checkKey(APP1_Markeer,0xFFE1)) return -1;
APP1.setStream(is);
APP1.setOffset(2);
APP1.setSize(10);
IFD_Offset.setStream(is);
IFD_Offset.setBaseKey(APP1);
IFD_Offset.setOffset(4);
IFD_Offset.setSize(4);
CountOfTag.setStream(is);
CountOfTag.setBaseKey(APP1);
CountOfTag.setOffset(IFD_Offset.getNumber(DefaultByteOrder));
CountOfTag.setSize(2);
FirstTagID.setStream(is);
FirstTagID.setBaseKey(CountOfTag);
FirstTagID.setOffset(0);
FirstTagID.setSize(2);
TagID=FirstTagID;
count=CountOfTag.getNumber(DefaultByteOrder);
while(count>0){
Type.setStream(is);
Type.setBaseKey(TagID);
Type.setOffset(0);
Type.setSize(2);
ValueOffset.setStream(is);
ValueOffset.setBaseKey(TagID);
ValueOffset.setOffset(6);
ValueOffset.setSize(4);

if(TagID.getValue()==0x010E){

if(Type.getValue()==2){
Tag t0;
t0.setName("Image_description" );
t0.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t0);
}

if(Type.getValue()==5){
Tag t1;
t1.setName("Image_description" );
t1.setValue(getStringByLink(TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t1);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t2;
t2.setName("Image_description" );
t2.setValue(ValueOffset.getString());
TagArray.push_back(t2);
}
}

if(TagID.getValue()==0x010F){

if(Type.getValue()==2){
Tag t3;
t3.setName("Make" );
t3.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t3);
}

if(Type.getValue()==5){
Tag t4;
t4.setName("Make" );
t4.setValue(getStringByLink(TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t4);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t5;
t5.setName("Make" );
t5.setValue(ValueOffset.getString());
TagArray.push_back(t5);
}
}

if(TagID.getValue()==0x0110){

if(Type.getValue()==2){
Tag t6;
t6.setName("Model" );
t6.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t6);
}

if(Type.getValue()==5){
Tag t7;
t7.setName("Model" );
t7.setValue(getStringByLink(TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t7);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t8;
t8.setName("Model" );
t8.setValue(ValueOffset.getString());
TagArray.push_back(t8);
}
}

if(TagID.getValue()==0x0112){

if(Type.getValue()==2){
Tag t9;
t9.setName("Orientation" );
t9.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t9);
}

if(Type.getValue()==5){
Tag t10;
t10.setName("Orientation" );
t10.setValue(getNumberByLink(DefaultByteOrder, TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t10);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t11;
t11.setName("Orientation" );
t11.setValue(ValueOffset.getNumber(DefaultByteOrder));
TagArray.push_back(t11);
}
}

if(TagID.getValue()==0x011A){

if(Type.getValue()==2){
Tag t12;
t12.setName("XResolution" );
t12.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t12);
}

if(Type.getValue()==5){
Tag t13;
t13.setName("XResolution" );
t13.setValue(getNumberByLink(DefaultByteOrder, TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t13);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t14;
t14.setName("XResolution" );
t14.setValue(ValueOffset.getNumber(DefaultByteOrder));
TagArray.push_back(t14);
}
}

if(TagID.getValue()==0x011B){

if(Type.getValue()==2){
Tag t15;
t15.setName("YResolution" );
t15.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t15);
}

if(Type.getValue()==5){
Tag t16;
t16.setName("YResolution" );
t16.setValue(getNumberByLink(DefaultByteOrder, TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t16);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t17;
t17.setName("YResolution" );
t17.setValue(ValueOffset.getNumber(DefaultByteOrder));
TagArray.push_back(t17);
}
}

if(TagID.getValue()==0x0132){

if(Type.getValue()==2){
Tag t18;
t18.setName("DateTime" );
t18.setValue(getCString(APP1,ValueOffset.getNumber(DefaultByteOrder)));
TagArray.push_back(t18);
}

if(Type.getValue()==5){
Tag t19;
t19.setName("DateTime" );
t19.setValue(getStringByLink(TagID,ValueOffset.getNumber(DefaultByteOrder),8));
TagArray.push_back(t19);
}

if(Type.getValue()==1||(Type.getValue()==3)||(Type.getValue()==4)){
Tag t20;
t20.setName("DateTime" );
t20.setValue(ValueOffset.getString());
TagArray.push_back(t20);
}
}
count-=1;
TagID.setOffset(10+TagID.getSize());
}

}
return 0;
}