pdfsync.cpp.svn-base
来自「SumatraPDF是一款小型开源的pdf阅读工具。虽然玲珑小巧(只有800多K」· SVN-BASE 代码 · 共 809 行 · 第 1/2 页
SVN-BASE
809 行
// Copyright William Blum 2008 http://william.famille-blum.org/
// PDF-source synchronizer based on .pdfsync file
// License: GPLv2
#include "SumatraPDF.h"
#include "PdfSync.h"
#include <assert.h>
#include <stdio.h>
#include "tstr_util.h"
#include "str_util.h"
#include <sys/stat.h>
#include <shlwapi.h>
// convert a coordinate from the sync file into a PDF coordinate
#define SYNCCOORDINATE_TO_PDFCOORDINATE(c) (c/65781.76)
// convert a PDF coordinate into a sync file coordinate
#define PDFCOORDINATE_TO_SYNCCOORDINATE(p) (p*65781.76)
// Test if the file 'filename' exists
bool FileExists( LPCTSTR filename ) {
struct _stat buffer ;
return 0 == _tstat( filename, &buffer );
}
Synchronizer *CreateSynchronizer(LPCTSTR pdffilename)
{
TCHAR syncfile[_MAX_PATH];
size_t n = _tcslen(pdffilename);
size_t u = dimof(PDF_EXTENSION)-1;
if (n>u && _tcsicmp(pdffilename+(n-u), PDF_EXTENSION) == 0 ) {
// Check if a PDFSYNC file is present
tstr_copyn(syncfile, dimof(syncfile), pdffilename, n-u);
tstr_cat_s(syncfile, dimof(syncfile), PDFSYNC_EXTENSION);
if (FileExists(syncfile))
return new Pdfsync(syncfile);
#ifdef SYNCTEX_FEATURE
// check if a compressed SYNCTEX file is present
tstr_copyn(syncfile, dimof(syncfile), pdffilename, n-u);
tstr_cat_s(syncfile, dimof(syncfile), SYNCTEXGZ_EXTENSION);
bool exist = FileExists(syncfile);
// check if a SYNCTEX file is present
tstr_copyn(syncfile, dimof(syncfile), pdffilename, n-u);
tstr_cat_s(syncfile, dimof(syncfile), SYNCTEX_EXTENSION);
exist |= FileExists(syncfile);
if(exist)
return new SyncTex(syncfile); // due to a bug with synctex_parser.c, this must always be
// the path to the .synctex file (even if a .synctex.gz file is used instead)
#endif
return NULL;
}
else {
DBG_OUT("Bad PDF filename! (%s)\n", pdffilename);
return NULL;
}
}
// Replace in 'pattern' the macros %f %l %c by 'filename', 'line' and 'col'
// the result is stored in cmdline
UINT Synchronizer::prepare_commandline(LPCTSTR pattern, LPCTSTR filename, UINT line, UINT col, PTSTR cmdline, UINT cchCmdline)
{
LPCTSTR perc;
size_t len = 0;
cmdline[0] = '\0';
LPTSTR out = cmdline;
size_t cchOut = cchCmdline;
while (perc = tstr_find_char(pattern, '%')) {
int u = perc-pattern;
tstr_copyn(out, cchOut, pattern, u);
len = tstr_len(out);
out += len;
cchOut -= len;
perc++;
if (*perc == 'f') {
tstr_copy(out, cchOut, filename);
}
else if (*perc == 'l') {
_sntprintf(out, cchOut, "%d", line);
}
else if (*perc == 'c') {
_sntprintf(out, cchOut, "%d", col);
}
else {
tstr_copyn(out, cchOut, perc-1, 2);
}
len = tstr_len(out);
out += len;
cchOut -= len;
pattern = perc+1;
}
tstr_cat_s(cmdline, cchCmdline, pattern);
return 1;
}
// PDFSYNC synchronizer
int Pdfsync::get_record_section(int record_index)
{
int leftsection = 0,
rightsection = record_sections.size()-1;
if (rightsection < 0)
return -1; // no section in the table
while (1) {
int n = rightsection-leftsection+1;
// a single section?
if (n == 1)
return leftsection;
else {
int split = leftsection + (n>>1);
int splitvalue = record_sections[split].firstrecord;
if (record_index >= splitvalue)
leftsection=split;
else
rightsection = split-1;
}
}
assert(0);
return -1;
}
FILE *Pdfsync::opensyncfile()
{
FILE *fp;
fp = fopen(syncfilename, "rb");
if (NULL == fp) {
DBG_OUT("The syncfile %s cannot be opened\n", syncfilename);
return NULL;
}
return fp;
}
// read a line from a stream (exclude the end-of-line mark)
LPTSTR ftgetline(LPTSTR dst, size_t cchDst, FILE *fp)
{
if (!_fgetts(dst, cchDst, fp))
return NULL;
LPTSTR end = dst+tstr_len(dst)-1;
while (*end == _T('\n') || *end == _T('\r'))
*(end--) = 0;
return dst;
}
int Pdfsync::scan_and_build_index(FILE *fp)
{
TCHAR jobname[_MAX_PATH];
ftgetline(jobname, dimof(jobname), fp); // get the job name from the first line
// replace star by spaces (somehow tex replaces spaces by stars in the jobname)
for(PTSTR rep = jobname; *rep; rep++) {
if (*rep==_T('*'))
*rep=_T(' ');
}
tstr_cat_s(jobname, dimof(jobname), _T(".tex"));
UINT versionNumber = 0;
int ret = _ftscanf(fp, "version %u\n", &versionNumber);
if (ret==EOF)
return 1; // bad line format
else if (versionNumber != 1)
return 2; // unknown version
srcfiles.clear();
// add the initial tex file to the file stack
src_file s;
s.first_recordsection = (size_t)-1;
s.last_recordsection = (size_t)-1;
tstr_copy(s.filename, dimof(s.filename), jobname);
#ifndef NDEBUG
s.closeline_pos = -1;
fgetpos(fp, &s.openline_pos);
#endif
srcfiles.push_back(s);
stack<size_t> incstack; // stack of included files
incstack.push(srcfiles.size()-1);
UINT cur_sheetNumber = (UINT)-1;
int cur_plinesec = -1; // index of the p-line-section currently being created.
int cur_recordsec=-1; // l-line-section currenlty created
record_sections.clear();
pdfsheet_index.clear();
CHAR buff[_MAX_PATH];
fpos_t linepos;
fgetpos(fp, &linepos);
char c;
while ((c = fgetc(fp)) && !feof(fp)) {
if (c!='l' && cur_recordsec!=-1) { // if a section of contiguous 'l' lines finished then create the corresponding section
#ifndef NDEBUG
this->record_sections[cur_recordsec].endpos = linepos;
#endif
cur_recordsec = -1;
}
if (c!='p' && cur_plinesec!=-1) { // if a section of contiguous 'p' lines finished then update the size of the corresponding p-line section
#ifndef NDEBUG
this->pline_sections[cur_plinesec].endpos = linepos;
#endif
cur_plinesec = -1;
}
switch (c) {
case '(':
{
// read the filename
ftgetline(buff, dimof(buff), fp);
PTSTR pfilename = buff;
int len = tstr_len(buff);
// if the filename contains quotes then remove them
if (str_startswithi(buff, "\"") && str_endswith_char(buff,'"')) {
pfilename++;
len-=2;
}
src_file s;
s.first_recordsection = (size_t)-1;
s.last_recordsection = (size_t)-1;
tstr_copyn(s.filename, dimof(s.filename), pfilename, len);
// if the file name extension is not specified then add the suffix '.tex'
if (tstr_find_char(pfilename, '.') == NULL) {
tstr_cat_s(s.filename, dimof(s.filename), _T(".tex"));
}
#ifndef NDEBUG
s.openline_pos = linepos;
s.closeline_pos = -1;
#endif
this->srcfiles.push_back(s);
incstack.push(this->srcfiles.size()-1);
}
break;
case ')':
#ifndef NDEBUG
if (incstack.top() != (size_t)-1)
this->srcfiles[incstack.top()].closeline_pos = linepos;
#endif
incstack.pop();
fscanf(fp, "\n");
break;
case 'l':
{
UINT columnNumber = 0, lineNumber = 0, recordNumber = 0;
if (fscanf(fp, " %u %u %u\n", &recordNumber, &lineNumber, &columnNumber) <2)
DBG_OUT("Bad 'l' line in the pdfsync file\n");
else {
if (cur_recordsec==-1){ // section not initiated yet?
record_section sec;
sec.srcfile = incstack.top();
sec.startpos = linepos;
sec.firstrecord = recordNumber;
record_sections.push_back(sec);
cur_recordsec = record_sections.size()-1;
}
#ifndef NDEBUG
record_sections[cur_recordsec].highestrecord = recordNumber;
#endif
assert(incstack.top() != (size_t)-1);
if (this->srcfiles[incstack.top()].first_recordsection == (size_t)-1)
this->srcfiles[incstack.top()].first_recordsection = cur_recordsec;
this->srcfiles[incstack.top()].last_recordsection = cur_recordsec;
}
}
break;
case 'p':
{
if (fgetc(fp)=='*')
fgetc(fp);
UINT recordNumber = 0, xPosition = 0, yPosition = 0;
fscanf(fp, "%u %u %u\n", &recordNumber, &xPosition, &yPosition);
if (cur_plinesec==-1){ // section not initiated yet?
plines_section sec;
sec.startpos = linepos;
#ifndef NDEBUG
sec.endpos = -1;
#endif
pline_sections.push_back(sec);
cur_plinesec = pline_sections.size()-1;
assert(cur_sheetNumber != (UINT)-1);
pdfsheet_index[cur_sheetNumber] = cur_plinesec;
}
}
break;
case 's':
{
fscanf(fp, " %u\n", &cur_sheetNumber);
size_t maxsheet = pdfsheet_index.size();
if (cur_sheetNumber>=maxsheet) {
pdfsheet_index.resize(cur_sheetNumber+1);
for(size_t s=maxsheet;s<=cur_sheetNumber;s++)
pdfsheet_index[s] = (size_t)-1;
}
break;
}
default:
DBG_OUT("Malformed pdfsync file: unknown command '%c'\n",c);;
break;
}
fgetpos(fp, &linepos);
}
#ifndef NDEBUG
if (cur_recordsec!=-1)
this->record_sections[cur_recordsec].endpos = linepos;
if (cur_plinesec!=-1)
this->pline_sections[cur_plinesec].endpos = linepos;
#endif
assert(incstack.size()==1);
return 0;
}
int Pdfsync::rebuild_index()
{
FILE *fp = opensyncfile();
if (!fp)
return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED;
scan_and_build_index(fp);
fclose(fp);
return Synchronizer::rebuild_index();
}
UINT Pdfsync::pdf_to_source(UINT sheet, UINT x, UINT y, PTSTR srcfilepath, UINT cchFilepath, UINT *line, UINT *col)
{
if (this->is_index_discarded())
rebuild_index();
FILE *fp = opensyncfile();
if (!fp)
return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED;
// distance to the closest pdf location (in the range <PDFSYNC_EPSILON_SQUARE)
UINT closest_xydist = (UINT)-1,
closest_xydist_record = (UINT)-1;
// If no record is found within a distance^2 of PDFSYNC_EPSILON_SQUARE
// (closest_xydist_record==-1) then we pick up the record that is closest
// vertically to the hit-point.
UINT closest_ydist = (UINT)-1, // vertical distance between the hit point and the vertically-closest record
closest_xdist = (UINT)-1, // horizontal distance between the hit point and the vertically-closest record
closest_ydist_record = (UINT)-1; // vertically-closest record
// find the entry in the index corresponding to this page
if (sheet>=pdfsheet_index.size()) {
fclose(fp);
return PDFSYNCERR_INVALID_PAGE_NUMBER;
}
// read all the sections of 'p' declarations for this pdf sheet
fpos_t linepos;
for(size_t cur_psection = pdfsheet_index[sheet];
(cur_psection<this->pline_sections.size())
&& ((sheet<pdfsheet_index.size()-1 && cur_psection < pdfsheet_index[sheet+1])
|| sheet==pdfsheet_index.size()-1) ;
cur_psection++) {
linepos = this->pline_sections[cur_psection].startpos;
fsetpos(fp, &linepos);
int c;
while ((c = fgetc(fp))=='p' && !feof(fp)) {
// skip the optional star
if (fgetc(fp)=='*')
fgetc(fp);
// read the location
UINT recordNumber = 0, xPosition = 0, yPosition = 0;
fscanf(fp, "%u %u %u\n", &recordNumber, &xPosition, &yPosition);
// check whether it is closer that the closest point found so far
UINT dx = abs((int)x - (int)SYNCCOORDINATE_TO_PDFCOORDINATE(xPosition));
UINT dy = abs((int)y - (int)SYNCCOORDINATE_TO_PDFCOORDINATE(yPosition));
UINT dist = dx*dx + dy*dy;
if (dist<PDFSYNC_EPSILON_SQUARE && dist<closest_xydist) {
closest_xydist_record = recordNumber;
closest_xydist = dist;
}
else if ((closest_xydist == (UINT)-1) && ( dy < PDFSYNC_EPSILON_Y ) && (dy < closest_ydist || (dy==closest_ydist && dx<closest_xdist))) { closest_ydist_record = recordNumber;
closest_ydist = dy;
closest_xdist = dx;
}
fgetpos(fp, &linepos);
}
assert(linepos == this->pline_sections[cur_psection].endpos);
}
UINT selected_record = closest_xydist_record!=(UINT)-1 ? closest_xydist_record : closest_ydist_record;
if (selected_record == (UINT)-1) {
fclose(fp);
return PDFSYNCERR_NO_SYNC_AT_LOCATION; // no record was found close enough to the hit point
}
// We have a record number, we need to find its declaration ('l ...') in the syncfile
// get the record section containing the record declaration
int sec = this->get_record_section(selected_record);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?