Как выдрать картинку из RichText

  • Автор темы CodeGuy
  • Дата начала
C

CodeGuy

#1
Здравствуйте!

Понадобилось сформировать небольшой отчёт по базе Lotus, которая состоит из документов с набором полей, одним из которых является Foto (его тип Item.RICHTEXT). Если выделить мышкой и нажать "Копировать" (Ctrl + C), то картинка отлично копируется и вставляется в Word, например. Так как документов много, а руками делать операцию сотни раз не хочется, то было принято решение набросать небольшую программу на Java, которая это сделает.

После нескольких мучений у меня получилось создать локальное приложение на Java (с помощью Notes.jar), которое соединяется с удалённым сервером, открывает нужную базу, осуществляет выборку документов и данных из них. Единственная проблема - в картинке, собственно, из-за неё всё и затевалось.
Итак, есть Item, тип которого Item.RICHTEXT, количество {doc links, file attachments, OLE objects, sections, tables, table cells, text paragraphs, text runs} равно нулю. Item.GetValueLength() возвращает 39708, а родной клиент Lotus пишет следующее:
Имя поля: Foto
Тип данных: Форматируемый текст
Размер данных: 39706 байт
Порядковый номер: 1
Код дубликата: 0
Флаги поля:

Помогите с советом, как выдрать картинку? Какой использовать метод? Как она там хранится?


P.S. Сейчас умею использовать классы из Notes.jar с обычным javac, есть клиентский доступ к серверу и базе Lotus, дизайнера нет и запускать код в клиенте Lotus не могу.
 
A

allex

#2
Если я правильно понял суть твоей задачи, тебе нужно из RT поля взять картинку и поместить в Word - документ.

Этот код делает как раз то что нужно
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Разворачивающийся текст</div></div><div class="sp-body"><div class="sp-content">
Код:
Sub Click(Source As Button)
Dim session As New notessession, db As notesdatabase, view As notesview
Dim rtitem As NotesRichTextItem	
Dim rtitem2 As NotesRichTextItem
Dim rtitem3 As NotesRichTextItem
'Dim rtitem2 As NotesItem
'Dim rtitem3 As NotesItem
Dim object As NotesEmbeddedObject
'Dim uidoc As NotesUIDocument

'Dim ui_doc As String
Dim n_kk As String
Dim date_kk As String
Dim postavleno_na_kontr As String
Dim n_doc_kk As String
Dim date_n_doc_kk As String

Dim nazvanie_doc As String
Dim soderjanie_poruch As String
Dim nazvanie_doc_ As String
Dim soderjanie_poruch_ As String

Dim plane_date_isp As String
Dim fakte_date_isp As String
Dim vid_predost_inf As String
Dim kurator_kk As String
Dim phone_kurator_kk As String
Dim otvet As String
Dim data As String
Dim doljnost_fio_telefon As String

Dim word As Variant
Dim worddoc As Variant

Set w = New NotesUiWorkspace
Set db = session.currentdatabase
Set uidoc = w.CurrentDocument
Set docA = uidoc.Document

'******** присвоение переменным значение полей из "Информация о заявителе"
n_kk = uidoc.FieldGetText("n_kk")	
date_kk = 
postavleno_na_kontr = 
n_doc_kk = 
..............

'******* получение файла шаблона и сохранение его на диск	
Set view = db.getview( "BDconf" )
Set doc=view.GetFirstDocument
Set rtitem = doc.GetFirstItem( "kk" )
Forall o In rtitem.EmbeddedObjects
If ( o.Type = EMBED_ATTACHMENT ) Then	
Call o.ExtractFile( "c:\" & o.Source )
FileName$ = o.Source 
End If
End Forall
'******* получение файла шаблона и сохранение его на диск

'******* получение шаблона подписи и сохранение его на диск		
Set view = db.getview("Pictures")
Set doc = view.getdocumentbykey("test")

'********сохранение картинки на диск
Set rtitem = doc.GetFirstItem( "Body" )
Forall o In rtitem.EmbeddedObjects
If ( o.Type = EMBED_ATTACHMENT ) Then
Call o.ExtractFile( "c:\" & o.Source )
PictureName$ = o.Source
End If
End Forall	

'******* Создание объекта Word:
Set word = CreateObject("Word.Application") 'Create Word object
Call word.documents.add("c:\" & FileName$) 'Create a new document based on the template 
word.visible = True
Set worddoc = word.activedocument 'Get a handle for the active document


'****** вставка пидписи
Set myRange = word.ActiveDocument.Tables(3).Rows(1).cells(1).Range
myRange.Select
Call word.selection.InlineShapes.AddPicture ("c:\" & PictureName$, False, True)

'********* Ответ в табличку
Set myRange1 = word.ActiveDocument.Tables(2).Rows(1).cells(1).Range
If worddoc.Bookmarks.Exists("otvet1") = False Then Exit Sub 'если закладки нет выходим
Call word.Selection.GoTo(-1,,, "otvet1") ' переходим на указаную закладку
Call Word.Selection.TypeText(otvet)


'******** записывание в поле worda значения переменной
worddoc.FormFields("n_kk").result = n_kk
....................

name_file= "c:\" + " Контрольная карточка"+"№"+ n_kk +".rtf"
worddoc.saveas( name_file ) 'сохранение документа Word на диск

Call docA.save(True,True)

End Sub
 
C

CodeGuy

#3
allex
Спасибо за пример кода. Проверить смогу только вечером, но сомневаюсь, что заработает, так как у меня в поле Item ВООБЩЕ НЕТ EmbeddedObjects, а здесь они являются ключевыми. :)

Собственно меня это и смущает, так как Ctrl+C работает, а как он сохранён - не ясно.

Может быть есть метод, который содержимое RichText поля скопирует в буфер? С таким попробую справиться.
 

Omh

Lotus team
04.07.2007
2 210
1
#4
Если через буфер, то можно сделать uidoc.Goto + uidoc.Copy
Но это только на форграунде и в едит моде.

Ещё можно выкинуть док в DXL и оттуда декодировать картинку.
 
C

CodeGuy

#5
Если через буфер, то можно сделать uidoc.Goto + uidoc.Copy
Но это только на форграунде и в едит моде.

Ещё можно выкинуть док в DXL и оттуда декодировать картинку.
"На форграунде" - это мне нужно внедрять в базу Lotus мою программу, так? Если да, то не подходит.

По поводу DXL ничего пока ничего не знаю, так как о программировании в Lotus узнал только вчера. Есть ли где-нибудь уже готовый пример подобной операции?
 

NetWood

Lotus team
17.04.2008
372
18
#7
'********сохранение картинки на диск
Set rtitem = doc.GetFirstItem( "Body" )
Forall o In rtitem.EmbeddedObjects
If ( o.Type = EMBED_ATTACHMENT ) Then
Call o.ExtractFile( "c:\" & o.Source )
Ха! Суть проблемы в том что картинка НЕ ЯВЛЯЕТСЯ НИ АТТАЧЕМ НИ ОБЬЕКТОМ. Увы. Были бы аттачи :)
 

Omh

Lotus team
04.07.2007
2 210
1
#9
Тебе надо:
1. Выгрузить документ в DXL (это простой текстовой XML файл)
2. По тегам найти картинку
3. Декодировать картинку (в DXL она хранится закодированая в base64)

Ключевые слова:
1. NotesDXLExporter
2. NotesDOMParser, GetElementsByTagName
3. NotesMIMEEntity или сторонними програмами.

Если только вчера начал программит в лотус, можешь и не осилить :)
 

Kee_Keekkenen

Well-known member
05.09.2006
639
4
#10
А чем же является картинка??? Или этого никто вообще не знает? Неужели все работают лишь с plain text?
сперва проверь, а после пиши.. если картинка вставлен как изображение, а не как файл, то в RTF поле нет никаких embeded объектов..
 

Omh

Lotus team
04.07.2007
2 210
1
#11
Kee_Keekkenen
Кажись, CodeGuy и не утверждал, что у него embedded object'ы...
 
C

CodeGuy

#12
сперва проверь, а после пиши.. если картинка вставлен как изображение, а не как файл, то в RTF поле нет никаких embeded объектов..
что проверить? у меня точно нет никаких вложений и embedded objects.

сейчас открыт клиент Lotus, куда смотреть?
 
C

CodeGuy

#13
Тебе надо:
1. Выгрузить документ в DXL (это простой текстовой XML файл)
2. По тегам найти картинку
3. Декодировать картинку (в DXL она хранится закодированая в base64)

Ключевые слова:
1. NotesDXLExporter
2. NotesDOMParser, GetElementsByTagName
3. NotesMIMEEntity или сторонними програмами.

Если только вчера начал программит в лотус, можешь и не осилить ;)
СПАСИБО!

1. Экспорт отдельного документа прошёл на ура, только пришлось использовать DxlExporter, так как NotesDXLExporter оказался неизвестен. Фотографию ручками вытащил и декодировал - появился честный jpeg, поэтому метод через пятую точку будет работать (разобрать-то этот xml смогу как угодно).

Заинтересовало, что при экспорте поле описывается следующим образом


<item name='Foto'><richtext>
<pardef id='1'/>
<par def='1'>
<picture width='118px' height='157px'><jpeg>
/9j/4AAQSkZJRgABAgEAZABkAAD/4RPjRXhpZgAATU0
... очень много букв ...
+9ybkiG2uMPxQ5Uc7Z1pwP/Z
</jpeg></picture></par>
<par def='1'/>
</richtext></item>

То есть на лицо пара параграфов (выделил цветом), однако элементов RichTextItem.RTELEM_TYPE_TEXTPARAGRAPH или RichTextItem.RTELEM_TYPE_TEXTRUN у меня вообще нет.

Всё-таки хочу попробовать сделать прямым методом через голову, поэтому вопрос:
Каким образом можно развернуть параграф, то есть получить доступ к содержимому первого параграфа элемента richtext? (здесь речь про стандартный интерфейс, а не XML)
 

Omh

Lotus team
04.07.2007
2 210
1
#14
Ты чем парсишь xml и вообще, зачнм "развернуть" параграф.
Я слегка не понял вопроса...

Только не забудь, что в DXL картинки погут лежать под тегами gif, jpeg и bmp.
 
C

CodeGuy

#15
Omh
Я сейчас сделал полную выгрузку всей базы (здесь "плюс" ibm за применимость метода, хотя жаль, что к отдельному элементу не применим). Теперь могу хоть offline разбирать, хоть онлайн. К этому вопросу ещё вернусь.

К сожалению, метод через XML немного коряв, так как похож на копирование файла с помощью печати и последующего распознавания. Мне сложно принять, что десяток лет картинки можно из richtext полей вытаскивать только с помощью XML-подобных операций. Пока нигде не встречал ответа, каким же образом картинка представлена в richtext, поэтому остаются лишь косвенные признаки. Здесь и наткнулся на "<pardef id='1'/> <par def='1'>" в DXL. Откуда они взялись? Это следствие упаковки изображения в поле или побочный эффект преобразования в DXL? Из каких же элементов состоит RichTextItem, если вообще ничего не возвращается?

Даже получаемый GetInputStream() поток пуст. Почему в IBM просто не могут вернуть представление, совпадающее с возвращаемой длиной элемента?
 

Omh

Lotus team
04.07.2007
2 210
1
#16
Вообще pardef это элемент начала параграфа.
В параграфе может быть текст, или, как например тут, картинка.
Я думаю, при вставлении картинки автоматически делается параграф, который и виден в DXL-ке.

С картинками, которые не embedded, а inline в лотусе действително беда.
Особенно раньше была, ибо никаких методов, позволяющих получить картинку не было.
С появлением 6-ки стало получше.
Кстати говоря, получение картинки через DXL, на мой взгляд, на данный момент, один из самых прямых методов ;)

Кстати, DXLExporter может выгружать и один элемент: там входным параметром может быть и объект класса NotesDocument.
 
C

CodeGuy

#17
Omh
значит буду разбираться с DXL

Под элементом я имел в виду те объекты, из которых состоят документы, входящие в базу. DXLExporter имеет следующие сигнатуры
public String exportDxl(Database database) throws NotesException
public String exportDxl(Document document) throws NotesException
public String exportDxl(DocumentCollection documentcollection) throws NotesException
public String exportDxl(NoteCollection notecollection) throws NotesException
и применимость к Item у него отсутсвует. Не думаю, что пройдёт преобразование Item --> Document
 

NetWood

Lotus team
17.04.2008
372
18
#18
Интересный путь намечается. Даже интересно ;) Далее задача сделать представление из найденных itemoв-картинок. Вот вопрос. Ну найде все что надо через DXL и ?
Если только пошерстить все необходимые доки агентом и прописать имена картинок в доп поле и потом выводить. мб.
 
C

CodeGuy

#19
NetWood
У меня есть подозрение, что под "представление" подразумеваете некоторый специальный объект Lotus, так? И насколько могу судить, то агент - это программа, выполняемая внутри базы?

Так как у меня нет задачи замкнуть всё внутри Lotus, поэтому я спокойно пробегусь по базе и легко вставлю картинки и данные в таблицу Word-документа, а дальше выведу на печать.
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 567
263
#20
всю ветку не прочел, может чё не видел...
Файлы и, как подмножество, картинки НЕ ХРАНЯТСЯ В РТФ поле! Там только хинты и их замбнейлы

ДХЛ самый гибкий и правильный путь, ИМХО

дергалку аттачей и картинок (включая файловые пиктограммы) я выкладывал на интертрасте (вообще там есть много кода для этого) - LAX
да и VTD очень даже (XPath поддерживает)
http://web3.inttrust.ru/site/itforum.nsf/x...83?OpenDocument
http://web3.inttrust.ru/site/itforum.nsf/f...33;OpenDocument (ссылка унутря)

остальное...
текст находится в тегах <par>, его атрибуты <pardef> ссылаюся по id

тут гондыбал базку и сочинял способы конвертации MIME....
родной java:
String encoded=new sun.misc.BASE64Encoder().encode(URLDecoder.decode(xml,"UTF-8").getBytes());
нативная java (код приложен в LAX):
String encoded=Base64.encodeBytes(URLDecoder.decode(xml,"UTF-8").getBytes());

Нотусёвая (через ж...):
String encoded=encodeBASE64(URLDecoder.decode(xml,"UTF-8"));

и сама
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">ф-ция</div></div><div class="sp-body"><div class="sp-content">
Код:
	public String encodeBASE64(String s){
String result="";
try{
Document doc=db.createDocument();
MIMEEntity entity=doc.createMIMEEntity("temp");
Stream tmp=session.createStream();

tmp.writeText(URLDecoder.decode(s,"UTF-8"));
entity.setContentFromText(tmp, "text/plain;charset=UTF-8", MIMEEntity.ENC_NONE);
//			tmp.setPosition(0);
//			tmp.truncate();
entity.encodeContent(MIMEEntity.ENC_BASE64);
result=entity.getContentAsText();
tmp.recycle();
doc.recycle();
} catch(Exception e) {
printStackTrace(e);
}
return result;
}
URLDecode там никчему :) - мне лень чистить было
и это кодеры, декодеры аналогичны

Можно применить чисто джавовское решение (без Нотусёвого клиента) - поднять на сервере IIOP (на клиенте проблематично и бессмысленно ИМХО)
бился здесь: http://www.inttrust.ru/Site/itforum.nsf/al...DD?OpenDocument
но там были "мои особенности"
а просто - должно быть без траблов...

Не понимаю также и зацикленность на МС продукты - там сплошные АктивХ прочая хрень - а оно вам надо.
Опен Офис - куда как проще - это XML
пример на том же ресурсе, очень показательный (я делал для ворда подбное, используя АктивХе - некрасивое решение, в сравнении с этим):
http://www.inttrust.ru/site/itforum.nsf/f1...33;OpenDocument
кстати там есть и парсинг SAX - что тоже дает возможность обработки файлов (выше в ссылках вариант, в т.ч. - LAX - ищите по ссылкам)