Вставка изображения в RTF-файл

iFree

Нужно вставить BMP-шное изображение в конец или начало файла RTF. Конкретного метода не нагуглил как это сделать, узнал только, что сам RTF понимает лишь EMF изображения. Конвертировать BMP в EMF не проблема, если это действительно так.Подскажите пожалуйста, как вставить изображение в RTF файл? Может быть есть в C++ Builder стандартные функции в RichEdit или нечто подобное? Мне не удалось найти, либо поправьте, плохо гуглил.Очень жду ответа, спасибо большое!UP! ну неужели никто не подскажет?..
9 ответов

iFree

img = new TImage(RichEdit1);
img->Parent = RichEdit1;
img->Picture->LoadFromFile("C:\\test.bmp");


iFree

Да, картинка добавляется, но когда сохраняю с РичБокса - документ пустой. Моя задача на вход подать файл BMP, а на выходе получить RTF внутри с этой картинкой, вставленной в конец файла. Или можно просто в новосозданный файл RTF


iFree

стандартный RichEdit этого не умеетможно попытаться использовать компонент WordApplication c закладки MS Office, только предварительно надо в опциях-пакеты - отметить эти компоненты, по умолчанию они отключены из-за копирайтов


iFree

Тоже не вариант, погуглил,
WordApplication->Connect();// пытаемся связаться с Word
Надо чтобы был установлен на компе Word. Это не то, нужно, чтобы картинка в RTF впихивалась без привязки к столь громоздким библиотекам.Если бы просто узнать алгоритм и как вообще хранятся файлы в rtf, я ничего не нагуглил подобного Я бы просто втупую вставил бы по битикам картинку прямо в файл или в каком месте она там хранится. Но из RTF я лишь текст умею форматировать, а о картинках ничего нет.Может еще варианты? кто что подскажет? Выкопировки кода? буду очень благодаренЗЫ если же попытаюсь статически влинковать библиотеку Word'a, я могу только представить до каких размеров возрастет моя софтинка


iFree

погуглите про ричєдит версии 3нашлось у меня такое
У меня TurboC++ никакого КвикРепорта или тому подобного нет. Решил отчетики в RTF генерировать в ручтую. В TStrings просто записываю {\\rtf1\\ansi\\ и т.д. и т.п. Потом сохраняю с расширением rtf и все В принципе вполне все сносно - и шрифт разного размера и таблички многострочные. Все было хорошо до того момента пока не понадобилось вставлять картиночки. Код нашел. Проверил. ВордПад открывает. Но все оказалось не так то просто... ВордПад единственный кто открывает. Остальные программы (OO WordViewer Abiword и компоненты RichView, RichEdit) не хотят Причем если ВордПадом пересохранить все открвают...Вопрос во том как это починить?
      TStrings *rtf = new TStringList();
      Graphics::TBitmap *BMP;
      BMP = new Graphics::TBitmap();
      BMP=Image4->Picture->Bitmap; //в ВМР картиночка
       
                          unsigned InfoHeaderSize = 0, ImageSize = 0;
                          AnsiString InfoHeader, Image;
                          AnsiString hexpict;
                          int I;
                          AnsiString mychar;
       
                          // GetDIBSizes, GetDIB из модуля Graphics
                          // Получаем размер заголовка и изображения цветной картинки
                          GetDIBSizes(BMP->Handle, InfoHeaderSize, ImageSize);
       
                          InfoHeader.SetLength(InfoHeaderSize);
                          Image.SetLength(ImageSize);
       
                          // Получаем сам заголовок и цветную картинку
                          GetDIB(BMP->Handle, BMP->Palette, &((InfoHeader.c_str())[0]), &((Image.c_str())[0]));
       
                          //Длина строки
                          hexpict.SetLength((InfoHeader.Length() + Image.Length()) * 2);
       
                          I = 1;
                          for (int z = 0; z < InfoHeader.Length(); z++){
                              mychar = IntToHex((int)((InfoHeader.c_str())[z]), 2);
       
                              if (mychar.Length() == 1) mychar = "0" + mychar;
       
                              mychar=mychar.SubString(mychar.Length()-1, 2);
       
                              (hexpict.c_str())[I - 1] = (mychar.c_str())[0];
                              (hexpict.c_str())[I] = (mychar.c_str())[1];
       
                              I=I+2;
                          }
                          for (int z = 0; z < Image.Length(); z++){
                              mychar = IntToHex((int)((Image.c_str())[z]), 2);
       
                              mychar=mychar.SubString(mychar.Length()-1, 2);
       
                              if (mychar.Length() == 1) mychar = "0" + mychar;
       
                              (hexpict.c_str())[I - 1] = (mychar.c_str())[0];
                              (hexpict.c_str())[I] = (mychar.c_str())[1];
       
                              I=I+2;
                          }
       
       
       
                          rtf->Add("{\\rtf1\\ansi\\ansicpg1251\\deff0\\deflang1049{\\fonttbl{\\f0\\fswiss\\fcharset204{\\*\\fname Arial;}Arial CYR;}{\\f1\\froman\\fprq2\\fcharset204{\\*\\fname Times New Roman;}Times New Roman CYR;}}");
                          rtf->Add("");      
       
                          //далее вставляю текст и т.д.
       
                          rtf->Add("{\\pict\\dibitmap "+hexpict+" }\\pard");
       
                          //далее снова вставляю текст и т.д.
       
                          rtf->Add("}");
       
                          rtf->SaveToFile("file.rtf");
То что я делаю в ручную dibitmap , то как сохраняет при пересохранении вордпад wmetafile8В ручную {\pict \dibitmap ...... } WopdPad {\pict\wmetafile8 ...... } .... там и там ************ уже смотрел The Rich Text Format (RTF) Specification Вроде там dibitmap дозволенный формат...Видимо дело в том, что версии как RTF, так и RichEdit могут быть разные. Не знаю как в TurboC, а например в BCB6 и Delphi7 юзается древнейший RichEdit 1.0 (riched32.dll), хотя уже в Win98 поддерживался 2.0 (riched20.dll), а в WinXP и вовсе 4.1 (msftedit.dll). Разумеется в вордпаде юзается последняя версия, а в RichView\RichEdit возможно более древняя, не понимающая dibitmap в RTFДак в Турбе тоже что и в билдере 6 с Richedit Я уже с этим встречался. Чтоб таблички отображать красиво надо подключать с программой RICHED20.DLL таскать. А есть из 2003 офиса вытащит этот RICHED20.DLL дак даже мышкой можно ширину столбиков таблицы менять - круче ВордПада из ХР Плюс Юникод открывает Richedit тоже как хочет (в зависимости от 9х или 2000/XP).А с RTF вообще беда какаято Спецификация это одно а как оно обрабатывается разными программами это другое... Скачал http://www.trichview.com/rvfiles/rvedit.zip Сделал минималистические rtf с картинками. Оказалось что у меня тега \plain перед текстом не хватаетНо все равно если BMP по вышепредеденному коду, то чтобы Ворд вьювер понимал надо таким образом обозначать \par {\pict\dibitmap0\wbmwidthbytes52\picw103\pich36\picwgoal1545\pichgoal540 Этой программе http://www.trichview.com/rvfiles/rvedit.zip хватает и \par {\pict\dibitmap0 Ворд-паду вообще хоть как и без \plain можно. А ОпенОфис и Абиворд картинку не видит А вот если конвертнуть BMP в EMF - все утро придумывал Ж) и вставить как \par {\pict\emfblip\picw3219\pich1125\picwgoal1545\pichgoal545 то тут Ворд-Пад не видит, зато все остальные видятМожет кому пригодится простейший код BMP -> EMF для RTF ниже, примеры RTF прикрепил ЗЫ: Что-то идея генерировать RTF разонравилась.. буду пробывать подключать freereport к этой ТурбеС++
         Graphics::TBitmap *BMP = new Graphics::TBitmap();
         TMetafile *MF = new TMetafile();
         TMetafileCanvas *MFCanvas;
         BMP->LoadFromFile("default.bmp");
         MF->Height = BMP->Height;
         MF->Width = BMP->Width;
         try{
          MFCanvas=new TMetafileCanvas(MF, 0);
       
          MFCanvas->Draw(0, 0, BMP);
       
          delete MFCanvas;
       
          //MF->SaveToFile("default.emf");
         }catch( ... ){
          delete MFCanvas;
         }
         delete BMP;
       
         AnsiString hexpict;
         TMemoryStream *ST=new TMemoryStream;
         MF->SaveToStream(ST);
         delete MF;
         ST->Position=0;
         hexpict.SetLength(ST->Size*2);
         int ptr;
         AnsiString mychar;
         int I=1;
         for (int i = 0; i <ST->Size; i++) {
           ST->Read(&ptr,1);
           mychar=IntToHex((int) ptr, 2);
              if (mychar.Length() == 1) mychar = "0" + mychar;
       
              mychar=mychar.SubString(mychar.Length()-1, 2);
       
              (hexpict.c_str())[I - 1] = (mychar.c_str())[0];
            (hexpict.c_str())[I] = (mychar.c_str())[1];
            I=I+2;
         }
              TStrings *sfile = new TStringList();
       
              sfile->Add("{\\rtf1");
              sfile->Add("\\ansi\\ansicpg1251\\deff0\\deflang1049{\\fonttbl{\\f0\\fswiss\\fcharset204{\\*\\fname Arial;}Arial CYR;}{\\f1\\froman\\fprq2\\fcharset204{\\*\\fname Times New Roman;}Times New Roman CYR;}}");
       
            sfile->Add("\\pard\\qc\\b\\fs28Ура\\fs16\\b0\\par");
       
              sfile->Add("{\\pict\\emfblip "+hexpict+" }\\pard");
       
            sfile->Add("\\pard\\qc\\b\\fs28Ура\\fs16\\b0\\par");
              sfile->Add("}");
            sfile->SaveToFile("zz.rtf");
код не мой, не проверял


iFree

Гениально!! Все прекрасно работает! Спасибо большое!


iFree

У меня почему-то остаётся метафайл пустой. Сохранял отдельно - 0 байт.С метафайлом помогли разобраться. Удалять метаканву нужно перед загрузкой в поток. А вот, что эта часть кода у Вас значит?
         int ptr;
         AnsiString mychar;
         int I=1;
         for (int i = 0; i <ST->Size; i++) {
           ST->Read(&ptr,1);
           mychar=IntToHex((int) ptr, 2);
              if (mychar.Length() == 1) mychar = "0" + mychar;
       
              mychar=mychar.SubString(mychar.Length()-1, 2);
       
              (hexpict.c_str())[I - 1] = (mychar.c_str())[0];
            (hexpict.c_str())[I] = (mychar.c_str())[1];
            I=I+2;
         }
Зачем проверка на длину, если в IntToHex задаёте вывод в 2 символа? Ну, и следующая строчка кажется абсурдной. Зачем брать подстроку в 2 символа с конца, если, по идее, их всего 2? И потом посимвольно последовательно присваивать первый символ к первому, второй к второму. Не понимаю. Не будет ли равноценным этот более простой код? Или я что-то упустил?
        int tBt;
        for(int i=0; i<ST->Size; i++)
        {
            ST->Read(&tBt,1);
            hexpict+=IntToHex(tBt,2);
        }
Комбинация тегов {\pict\dibitmap0\wbmwidthbytes52\picw103\pich36\picwgoal1545\pichgoal540 из файла BMP2 отображается у меня во всех редакторах корректно. Остальные - где-то есть картинка, а где-то нет.Но {\pict\wmetafile8\picwgoal810\pichgoal1200, думаю, надёжнее.Только BYTE tBt, естественно.Я так понимаю, дело в кодировке. Метафайл, который отображается и в WordPad'е и в MS Word'e, при той же картине внутри имеет другой код. Заметил, что генерируемый мною текст кода начинается с 010000006C..., а тот, что сохраняется текстовыми процессорами - 010009... Кто знает, где про структуру метафайлов почитать можно?Так, от тестовых редакторов шапка перед моим кодом: 010009000003c607000008003c05000000003c05000026060f006e0a574d464301000000000001 00952300000000010000004c0a0000000000004c0a0000Ещё хвост отличается, но я там не нащупал стык пока.
        //В метафайл
        TMetafile *MF = new TMetafile();
        MF->Inch=MyForm1->PixelsPerInch;
        MF->MMWidth=254*ImageFon->Picture->Width/MF->Inch;
        MF->MMHeight=254*ImageFon->Picture->Height/MF->Inch;
        MF->SetSize(ImageFon->Width*1.1,
                    ImageFon->Height*1.1);
        //Рисуем
        TMetafileCanvas *tMetaCanv = new TMetafileCanvas(MF,0);
        tMetaCanv->Draw(0,0,ImageFon->Picture->Bitmap);
        delete tMetaCanv;//Удалять до загрузки в поток/файл!
        MF->SaveToFile("tmp_test.emf");
        //Получаем байты
        int KolB=GetEnhMetaFileBits((HENHMETAFILE)MF->Handle, 0, NULL);
        BYTE *tBuffer = new BYTE[KolB];
        GetEnhMetaFileBits((HENHMETAFILE)MF->Handle, KolB, tBuffer);
        //В строку
        AnsiString hexpict("");
        for(int i=0; i<KolB; i++)
        {
            hexpict+=IntToHex(tBuffer[i],2);
        }
        //Вывод
        TMP+="\\pard\\par{\\pict\n";
        TMP+=("\\picw"+IntToStr(MF->Width)+"\n");
        TMP+=("\\pich"+IntToStr(MF->Height)+"\n");
        TMP+=("\\picwgoal"+IntToStr(1440*MF->Width/MF->Inch)+"\n");
        TMP+=("\\pichgoal"+IntToStr(1440*MF->Height/MF->Inch)+"\n");
        TMP+=("\\wmetafile8\n"+hexpict+"}\\pard\\par\\par\n");//emfblip
        //Чистим
        delete [] tBuffer;
        delete MF;
TMP тут AnsiString, которую потом сохраним в rtf-файл. Видит картинку в Word'е, а вот в WordPad нет, как и в RichEdit с поддержкой OLE. Проблема в структуре метафайла, т.к. другие метафайлы видят все. Надеюсь, у кого-нибудь будут соображения по этой теме.


iFree

Хвост тоже нащупал!0400000003010800050000000b0200000000050000000c02570157010500000014020000000007 000000fc020000ffffff000000040000002d010000050000000102ffffff000400000002010200 040000002d0100000c00000040092100f0000000000000005a015a0100000000040000002d0100 0008000000fa0200000100000080808000040000002d0101000400000004010d00070000001804 4b014b010f000f0008000000fa0200000000000000000000040000002d01020008000000fa0200 0001000000c0c0c000040000002d0103000400000004010d000700000018041701170143004300 070000001804e200e200780078000500000014025e003601050000001302fc0024000500000014 022400fc0005000000130236015e000500000014020f00ad000500000013024b01ad0005000000 140224005e000500000013023601fc000500000014025e002400050000001302fc003601040000 002d01020004000000f0010300050000001402ad000f00040000002d0101000400000004010d00 050000001302ad004b010500000014020f00ad000500000013024b01ad001c000000fb02f5ff00 0000000000900100000001000000005461686f6d6100003f4700003f3f00006937213f3f123f3f 3f123f3f3f613f12040000002d010300050000000902808080000a000000320aa8004e01020000 0030b006000500050000001402a80059010c000000320a0100a800030000003930b00006000600 05000500000014020100b9000d000000320a4b01a300040000002d3930b0040006000600050005 00000014024******************************************************************* 1402a8001800040000002d01020007000000fc020100000000000000040000002d0104001c0000 00fb021000070000000000bc02000000cc0102022253797374656d000000000000000000000000 0000000000000000000000000000040000002d010500050000001402ad00ad0008000000fa0200 000200000080000000040000002d0106000400000004010d0007000000fc020000800000000000 040000002d01070005000000010280000000040000000201020005000000130236005500040000 002d01020004000000f001060008000000fa0200000100000080000000040000002d0106000400 000004010d000a000000240303005e003b005700400055003600040000002d01040004000000f0 010700040000002d01030005000000090280000000040000002d010400050000000102000000ff 040000000201010009000000320a270055000100000055bb070005000000140227005c00040000 002d01020004000000f0010600040000002d010400040000002d010500050000001402ad00ad00 08000000fa0200000200000000008000040000002d0106000400000004010d0007000000fc0200 00000080000000040000002d010700050000000102000080000400000002010200050000001302 b4005800040000002d01020004000000f001060008000000fa0200000100000000008000040000 002d0106000400000004010d000a000000240303006100af006200b7005800b400040000002d01 040004000000f0010700040000002d01030005000000090200008000040000002d010400050000 000102000000ff040000000201010009000000320ab40058000100000049bb0400050000001402 b4005c00040000002d010200040000002d010400040000002d010500030000000000Вот что это за "оболочка" для моего файла? Универсальная ли она?
The Windows GDI API provides a way to save and load metafiles on disk. This is the CreateMetafile(LPCTSTR filePath) function. But, using it for these purposes not a good idea. This way, a created metafile doesn't work with all versions of MS Word and Picture object (IPicture). To avoid these restrictions, you need to save the metafile in a placeable format. In this case, metafile bits are prefixed with a metafile header structure. This structure contains the metafile dimensions and an indication of the placeable metafile. Its size is 22 bytes. The structure is not defined in standard header files; thus, you need to define it explicitly. The next lines of code show how to use the WMFile class to save a placeable metafile. This part of the code can be used in conjunction with the previous one.
    WMFile wmfFile(metafile, extents.cx, extents.cy, FALSE);
    LPCTSTR filename = _T("test.wmf");
    wmfFile.save(fileName);
О, нужно копать в этом направлении.


iFree

Это может быть EMF+? Как конвертировать EMF в EMF+?