А не подскажут ли уважаемые специалисты, есть ли какая-нибудь возможность по COM поуправлять Adobe Acrobat Reader’ом? Интересует не только показать/напечатать, но и добавить комментарий (такая возможность есть в самой программе), поменять цвет объекта (у кого можно поменять цвет) и так далее. Задача его в свой ActiveX обернуть, а как непонятно.
И еще вопрос в PDF можно делать формы, помечать текст пометками и рисовать «от руки» поверх оригинального PDF. Все это можно сделать из вне через API Acrobat Reader?
Заранее спасибо.
Здравствуйте, liokumovich, Вы писали:
L>А не подскажут ли уважаемые специалисты, есть ли какая-нибудь возможность по COM поуправлять Adobe Acrobat Reader’ом? Интересует не только показать/напечатать, но и добавить комментарий (такая возможность есть в самой программе), поменять цвет объекта (у кого можно поменять цвет) и так далее. Задача его в свой ActiveX обернуть, а как непонятно.
L>И еще вопрос в PDF можно делать формы, помечать текст пометками и рисовать «от руки» поверх оригинального PDF. Все это можно сделать из вне через API Acrobat Reader?
В принципе можно, только для этого нужен не Reader, а полноценный Acrobat. Через COM много не сделаешь, нужен SDK — есть на adobe.com.
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
Здравствуйте, liokumovich
Reader'ом можно по DDE управлять. Года 4 назад я делал через него из своей программы печать pdf-ников в файл.
Если этот способ устраивает посмотри в сторону ::DdeInitialize(), ::DdeConnect(), ::DdeClientTransaction()
Ниже образец рабочего кода — как это делалось.
/////////////////////////////////////////////////////////////////
//
// FILE: AcroDDE.h
//
/////////////////////////////////////////////////////////////////
#ifndef __CLASS_ACROBAT_DDE__
#define __CLASS_ACROBAT_DDE__
#include <Ddeml.h>
class CAcroDDE
{
public:
CAcroDDE();
virtual ~CAcroDDE();
bool Connect( bool bShutDown );
void Disconnect() { if(m_ID) ::DdeUninitialize(m_ID), m_ID = 0; }
DWORD Print2File( const char* szPrn, const char* szSrc, const char* szDst );
protected:
bool ShutdownAcrobat();
private:
static HDDEDATA __stdcall DDECallback(uint uType, uint uFmt, HCONV hconv,
HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
DWORD dwData1, DWORD dwData2);
bool LaunchAcrobat();
private:
DWORD m_ID;
HCONV m_hConversation;
HSZ m_hszServerName;
HSZ m_hszTopicName;
PROCESS_INFORMATION
m_AcroInfo;
};
#endif //__CLASS_ACROBAT_DDE__
/////////////////////////////////////////////////////////////////
//
// FILE: AcroDDE.cpp
//
/////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "AcroDDE.h"
#define SHUTDOWN_DELAY 1000
#define MAX_TIMEOUT (1000 * 60 * 10) // 10 minutes
#define STEP_SIZE 200
#define REG_INSTALL_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\AcroRd32.exe"
/////////////////////////////////////////////////////////////////
//
// PURPOSE: Constructor
//
// PARAMETERS: none
//
/////////////////////////////////////////////////////////////////
CAcroDDE::CAcroDDE()
: m_ID(0)
{
ZeroMemory(&m_AcroInfo, sizeof(m_AcroInfo));
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: Destructor
//
/////////////////////////////////////////////////////////////////
CAcroDDE::~CAcroDDE()
{
ShutdownAcrobat();
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: launch acrobat reader
//
// PARAMETERS: none
//
// RETURN: status
//
/////////////////////////////////////////////////////////////////
bool CAcroDDE::LaunchAcrobat()
{
if( m_AcroInfo.hProcess )
{
TRACE("CAcroDDE::LaunchAcrobat, already started, handle %x", m_AcroInfo.hProcess );
return true;
}
HKEY hKey;
DWORD pathBuf[MAX_PATH + 1];
DWORD dwSize = MAX_PATH + 1;
bool bStat = false;
int nStat = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_INSTALL_KEY, 0, KEY_READ, &hKey);
if( nStat == ERROR_SUCCESS )
{
nStat = ::RegQueryValueEx(hKey, "", 0, 0, (unsigned char *)pathBuf, &dwSize);
::RegCloseKey(hKey);
if( nStat == ERROR_SUCCESS )
{
ZeroMemory(&m_AcroInfo, sizeof(m_AcroInfo));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
bStat = ::CreateProcess((const char*)pathBuf, 0, 0, 0, false,
NORMAL_PRIORITY_CLASS, 0, 0, &si, &m_AcroInfo)
!= 0;
}
}
return bStat;
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: terminate acrobat reader
//
// PARAMETERS: none
//
// RETURN: error status
//
/////////////////////////////////////////////////////////////////
bool CAcroDDE::ShutdownAcrobat()
{
if( m_AcroInfo.hProcess )
{
BOOL bStat = ::TerminateProcess(m_AcroInfo.hProcess, 0);
ZeroMemory(&m_AcroInfo, sizeof(m_AcroInfo));
TRACE("CAcroDDE::ShutdownAcrobat, status %d ", bStat);
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: standard callback for dde messages
//
// PARAMETERS: standard dde param
//
// RETURN: HDDEDATA
//
/////////////////////////////////////////////////////////////////
HDDEDATA __stdcall CAcroDDE::DDECallback(uint uType, uint uFmt, HCONV hconv,
HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
DWORD dwData1, DWORD dwData2)
{
TRACE("CAcroDDE::DDECallback entered");
return 0;
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: connect to Acrobat Reader by means of DDE
//
// PARAMETERS: bShutDown - need Reader's shutdown or not
//
// RETURN: status
//
/////////////////////////////////////////////////////////////////
bool CAcroDDE::Connect( bool bShutDown )
{
TRACE("CAcroDDE::Connect, called");
Disconnect();
if( bShutDown )
{
ShutdownAcrobat();
::Sleep(SHUTDOWN_DELAY);
}
if( !LaunchAcrobat() )
{
AsyncMessage("ERROR: cannot launch Acrobat Reader");
return false;
}
m_ID = 0;
if( DMLERR_NO_ERROR != ::DdeInitialize(&m_ID, &DDECallback, APPCMD_CLIENTONLY, 0) )
return false;
m_hszServerName = ::DdeCreateStringHandle(m_ID, "acroview", 0);
m_hszTopicName = ::DdeCreateStringHandle(m_ID, "control", 0);
assert( m_hszServerName && m_hszTopicName );
DWORD dwSleep = 0;
do
{
m_hConversation = ::DdeConnect(m_ID, m_hszServerName, m_hszTopicName, 0);
if( m_hConversation || (dwSleep > MAX_TIMEOUT) )
return false;
::Sleep( dwSleep += STEP_SIZE );
} while(true);
return m_hConversation != 0;
}
/////////////////////////////////////////////////////////////////
//
// PURPOSE: connect to Acrobat Reader by means of DDE
//
// PARAMETERS: szPrn - name of printer
// szSrc - source file to be printed
// szDst - out prn file name
//
// RETURN: error code ( 0 - success )
//
/////////////////////////////////////////////////////////////////
DWORD CAcroDDE::Print2File( const char* szPrn, const char* szSrc, const char* szDst )
{
ifstream is;
is.open(szSrc, ios_base::binary | ios_base::in);
if( !is.is_open() )
{
TRACE("CAcroDDE::Print2File, file %s not exists !", szSrc);
return 0;
}
is.close();
string sCmd = "[FilePrintTo(\"";
sCmd += szSrc;
sCmd += "\", \"";
sCmd += szPrn;
sCmd += "\", \"\", \"";
sCmd += szDst;
sCmd += "\")]";
// 0 attempt - suppose Reader already launched and DDE connected
// 1 attempt - DDE disconnect (if exist) and do new connection
// 2 attempt - hard error => restart Reader and do new connection
//
int nMaxAttempts = 3;
DWORD dwErr = 0;
for( int i=0; i<nMaxAttempts; i++ )
{
if(i) Connect( (i+1)==nMaxAttempts );
if( ::DdeClientTransaction((BYTE*)sCmd.c_str(), sCmd.length(),
m_hConversation, 0, CF_TEXT,
XTYP_EXECUTE, MAX_TIMEOUT, 0)
) return 0;
dwErr = ::DdeGetLastError(m_ID);
TRACE("CAcroDDE::Print2File, DDE error %X, attempt %d", dwErr, i);
}
return dwErr;
}
Подведу итоги моих собственных исследований.
Adobe Acrobat Reader имеет следующее COM API:
1) API, повторяющее минимальный набор DDE команд, которые знает Acrobat Reader. А именно: CloseAllDocs, DocClose,DocGoTo, DocGoToNameDest, DocOpen, FileOpenEx, FilePrintEx, FilePrintSilentEx, FilePrintToEx, and AppExit
Причем, это API было сделано исключительно для того, чтобы PDF можно было открывать в MS IE; как следствие API недокументированно и не “саппортится”.
2) Полноценное COM API, которое может все. Оно доступно только для plug-in'ов и только по специальной лицензии от Adobe (стоит от $1000 до $2500) . Эта лицензия очень много чего запрещает. Например, нельзя сделать plug-in, который вытащит наружу управление (navigation). При этом надо сначала написать свой plug-in используя Acrobat Professional (в нем COM API доступен), а затем подать заявку на лицензирование.
На сайте Adobe лежит много документов при попытке скачать который просят ввести логин/пароль, и при этом говорят, что регистрация бесплатна. Так вот, во всех случаях, с которыми столкнулся я, «уровня дружественности», который дает бесплатная регистрация, оказалось не достаточно для доступа к запрашиваемым документам.