TeXnicCenter and Pdfpos

I’ve been using TeXnicCenter over the past month to compose my LaTeX files. I really like it the only problem is it comes with limited support for interacting with Acrobat. Acrobat doesn’t play nice with other software programs because it locks any pdf that it is viewing, which prevents any other program from accessing the same file. Thus when I try to modify the pdf using TeXnicCenter, pdflatex fails.

The current solution is to issue DDE commands to get Acrobat to close the pdf before it is written to and reopen it after it is finished. However, because the Acrobat DDE interface it is difficult to make Acrobat open the pdf to the place you want it to. For example, say you want to modify line 400 in your LaTeX file and look at the results in the pdf. Using the current solution, every time you open the pdf, you’d have to scroll down several pages until you find the one where line 400 is.

The ideal solution would be for TeXnicCenter to direct the pdf to open to the exact spot where line 400 is. However, the association of tex lines with pdf pages is not an easy one to estimate. So, I came up with an alternative solution that should work for most cases.

My solution, pdfpos, is to record on close where the pdf is currently being viewed and then scroll to that location when the new version is opened. Therefore, if you are currently viewing the location you wish to modify the new pdf will scroll to that location when it is opened.

My program uses the OLE interface to Acrobat which is more powerful than the DDE interface. I wrote pdfpos in Perl, and thus it requires Perl to run. (Get ActivePerl.) In addition you need the modules Win32::OLE and Win32::GuiTest. Visual Basic would offer a more natural implementation of this technique, but I don’t know VB.

To integrate pdfpos into TeXnicCenter simply create a new build profile, copied from one of your existing pdf profiles. Then change the viewer from “path\to\acrobat.exe” to “path\to\pdfpos.bat”. Then under view and forward search select command line argument and enter view "%bm.pdf". Similarly under close select command line argument and enter close "%bm.pdf". (Download an importable TeXnicCenter build profile.)

Download pdfpos.bat.

The code is below the fold.

#!perl -w
############################################################################
# pdfpos version 1.0                                                       #
# Copyright (c) 2006 Reed A. Cartwright (reed.scit.us) All Rights Reserved #
# Released under the Perl Artistic License                                 #
# http://www.perl.com/pub/a/language/misc/Artistic.html                    #
############################################################################
#                                                                          #
# Usage:                                                                   #
#     pdfpos view <filename>                                               #
#     pdfpos close <filename>                                              #
#                                                                          #
############################################################################

use strict;
use Win32::OLE;
use Win32::GuiTest qw(FindWindowLike SetForegroundWindow);

my %cmd_hash = (
	'view' => \&pdfview,
	'close' => \&pdfclose,
);

my $cmd = shift(@ARGV);
unless(exists $cmd_hash{$cmd})
{
	print "Command $cmd not recognized.\n";
	exit();
}
$cmd_hash{$cmd}->(@ARGV);

sub pdfview
{
	my $f = shift;
	print "Viewing $f\n";
	my $app = Win32::OLE->new('AcroExch.App');
	$app->Show();
	my $avdoc = Win32::OLE->new('AcroExch.AVDoc');
	$avdoc->Open($f,$f);
	if(open(POS, "${f}pos"))
	{
		my @pos = <POS>;
		chomp(@pos);
		my $view = $avdoc->GetAVPageView();
		$avdoc->SetViewMode($pos[0]);
		$view->ZoomTo($pos[1],$pos[2]);
		$view->Goto($pos[3]);
		$view->ScrollTo($pos[4],$pos[5]);
		close(POS);
	}
	$avdoc->BringToFront();
	#force focus to acrobat
	#system('C:\PROGRAMS\ADOBE\ACROBAT 6.0\ACROBAT\ACROBAT.EXE');
	my @windows = FindWindowLike( undef, "Acrobat", "", undef, 1 );
	SetForegroundWindow($windows[0]);
}

sub pdfclose
{
	my $f = shift;
	print "Closing $f\n";
	my $app = Win32::OLE->new('AcroExch.App');
	my $num = $app->GetNumAVDocs();
	return unless($num);
	my $avdoc;
	for(my $i=0;$i<$num;++$i)
	{
		$avdoc = $app->GetAVDoc($i);
		last if($avdoc->GetTitle() eq $f);
		$avdoc = undef;
	}
	return unless($avdoc);
	if(open(POS, ">${f}pos"))
	{
		local $\ = "\n";
		my $view = $avdoc->GetAVPageView();
		print POS $avdoc->GetViewMode();
		print POS $view->GetZoomType();
		print POS $view->GetZoom();
		print POS $view->GetPageNum();
		print POS $view->GetAperture()->Left;
		print POS $view->GetAperture()->Top;
		close(POS);
	}		
	$avdoc->Close(0);	
}
TrackBack URL for this entry: http://scit.us/cgi-bin/mt/mt-tb.fcgi/699.

Use KwickXML Formatting to markup your comments, acceptable tags: <b> <blockquote> <br> <code> <em> <email> <h1> <h2> <h3> <h4> <h5> <h6> <i> <li> <list> <ol> <p> <qref> <quote> <s> <strong> <sub> <sup> <u> <ul> <url>. You may need to refresh before you will see your comment.




Remember personal info?

  


Posted by John Wilkins on March 3, 2006 4:51 AM

Or, you could get a Mac and use the inbuilt PDF Quartz, or use LaTex2PDF on unix…

[Sorry, I have to be smug. It’s in the warranty conditions for buying a Mac]

Posted by marcus leinweber on March 21, 2006 8:14 AM

but it does not work with the reader, right?

m.

Posted by Reed A. Cartwright on March 21, 2006 5:07 PM

I haven’t tested it with the reader, but as far as I can tell all the OLE commands that I use are also in reader.

Posted by Kenny on May 5, 2006 1:52 AM

If I correctly understand what you are trying to do, I think this wikipedia page (http://en.wikipedia.org/wiki/TeXnicCenter) covers this technique using DDE.

Thanks for pointing me in the right direction though (it was really frustrating to have to manually close the pdf and then find the place). I didn’t realize this was possible before.

Posted by Theo Hopman on March 31, 2007 7:10 PM

Sadly, according to the Adobe developer guide at http://www.adobe.com/devnet/acrobat/pdfs/iac_dev…, “Adobe Reader does not support OLE automation, except for the PDF browser controls provided in the AcroPDF object.”

I do hope that you can prove my interpretation of the documentation wrong :)

THeo

Posted by Reed A. Cartwright on March 31, 2007 9:01 PM

Nope, Theo, you are correct. It only works with Acrobat Pro, not the reader.

Posted by Matthias on July 19, 2007 8:16 PM

Opening (view) works, closing (close) does not…
Acrobat 8.1 Professional.
Closing does not trow an error but Acrobat keeps the pdf open.

-Matthias

Posted by Matthias on July 19, 2007 9:25 PM

Me again. I dived into the API documentation to fix the problem.
Apparently Acrobat 8.1 gives the real window title in response to $avdoc->GetTitle(). Meaning:
“title_of_the_file - Adobe Acrobat Professional”.
Because of the “ - Adobe Acrobat Professional” part it never equals $f.
But I fixed it: http://wilma.vub.ac.be/~mstevens/latex/pdfpos.ba…

(it is somewhat hackish I’ve been learning Perl on the fly here :-).

Hope this helps others too!
Greetings,

-Matthias

Posted by Reed A. Cartwright on July 19, 2007 11:52 PM

Matthias,

Thanks for the bug report. I just got 8.1 and hadn’t tested pdfpos on it. I’ve just produced pdfpos 1.1 [DL], but I did the patch differently than you. I use a regex to see if the window name contains the file name instead of seeing if they are equal.