// Find a record corresponding to the given source file, line number and optionally column number. // (at the moment the column parameter is ignored) // // If there are several *consecutively declared* records for the same line then they are all returned. // The list of records is added to the vector 'records' // // If there is no record for that line, the record corresponding to the nearest line is selected // (within a range of EPSILON_LINE) // // The function returns PDFSYNCERR_SUCCESS if a matching record was found. UINT Pdfsync::SourceToRecord(const WCHAR* srcfilename, UINT line, UINT col, Vec<size_t> &records) { if (!srcfilename) return PDFSYNCERR_INVALID_ARGUMENT; ScopedMem<WCHAR> srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.Set(str::Dup(srcfilename)); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; // find the source file entry size_t isrc; for (isrc = 0; isrc < srcfiles.Count(); isrc++) if (path::IsSame(srcfilepath, srcfiles.At(isrc))) break; if (isrc == srcfiles.Count()) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (fileIndex.At(isrc).start == fileIndex.At(isrc).end) return PDFSYNCERR_NORECORD_IN_SOURCEFILE; // there is not any record declaration for that particular source file // look for sections belonging to the specified file // starting with the first section that is declared within the scope of the file. UINT min_distance = EPSILON_LINE; // distance to the closest record size_t lineIx = (size_t)-1; // closest record-line index for (size_t isec = fileIndex.At(isrc).start; isec < fileIndex.At(isrc).end; isec++) { // does this section belong to the desired file? if (lines.At(isec).file != isrc) continue; UINT d = abs((int)lines.At(isec).line - (int)line); if (d < min_distance) { min_distance = d; lineIx = isec; if (0 == d) break; // We have found a record for the requested line! } } if (lineIx == (size_t)-1) return PDFSYNCERR_NORECORD_FOR_THATLINE; // we read all the consecutive records until we reach a record belonging to another line for (size_t i = lineIx; i < lines.Count() && lines.At(i).line == lines.At(lineIx).line; i++) records.Push(lines.At(i).record); return PDFSYNCERR_SUCCESS; }
int SyncTex::DocToSource(UINT pageNo, PointI pt, ScopedMem<WCHAR>& filename, UINT *line, UINT *col) { if (IsIndexDiscarded()) { if (RebuildIndex() != PDFSYNCERR_SUCCESS) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; } CrashIf(!this->scanner); // Coverity: at this point, this->scanner->flags.has_parsed == 1 and thus // synctex_scanner_parse never gets the chance to freeing the scanner if (synctex_edit_query(this->scanner, pageNo, (float)pt.x, (float)pt.y) <= 0) return PDFSYNCERR_NO_SYNC_AT_LOCATION; synctex_node_t node = synctex_next_result(this->scanner); if (!node) return PDFSYNCERR_NO_SYNC_AT_LOCATION; const char *name = synctex_scanner_get_name(this->scanner, synctex_node_tag(node)); if (!name) return PDFSYNCERR_UNKNOWN_SOURCEFILE; bool isUtf8 = true; filename.Set(str::conv::FromUtf8(name)); TryAgainAnsi: if (!filename) return PDFSYNCERR_OUTOFMEMORY; // undecorate the filepath: replace * by space and / by \ str::TransChars(filename, L"*/", L" \\"); // Convert the source filepath to an absolute path if (PathIsRelative(filename)) filename.Set(PrependDir(filename)); // recent SyncTeX versions encode in UTF-8 instead of ANSI if (isUtf8 && !file::Exists(filename)) { isUtf8 = false; filename.Set(str::conv::FromAnsi(name)); goto TryAgainAnsi; } *line = synctex_node_line(node); *col = synctex_node_column(node); return PDFSYNCERR_SUCCESS; }
int SyncTex::SourceToDoc(const WCHAR* srcfilename, UINT line, UINT col, UINT *page, Vec<RectI> &rects) { if (IsIndexDiscarded()) if (RebuildIndex() != PDFSYNCERR_SUCCESS) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; assert(this->scanner); ScopedMem<WCHAR> srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.Set(str::Dup(srcfilename)); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; bool isUtf8 = true; char *mb_srcfilepath = str::conv::ToUtf8(srcfilepath); TryAgainAnsi: if (!mb_srcfilepath) return PDFSYNCERR_OUTOFMEMORY; int ret = synctex_display_query(this->scanner, mb_srcfilepath, line, col); free(mb_srcfilepath); // recent SyncTeX versions encode in UTF-8 instead of ANSI if (isUtf8 && -1 == ret) { isUtf8 = false; mb_srcfilepath = str::conv::ToAnsi(srcfilepath); goto TryAgainAnsi; } if (-1 == ret) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (0 == ret) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; synctex_node_t node; int firstpage = -1; rects.Reset(); while ((node = synctex_next_result(this->scanner)) != NULL) { if (firstpage == -1) { firstpage = synctex_node_page(node); if (firstpage <= 0 || firstpage > engine->PageCount()) continue; *page = (UINT)firstpage; } if (synctex_node_page(node) != firstpage) continue; RectD rc; rc.x = synctex_node_box_visible_h(node); rc.y = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node); rc.dx = synctex_node_box_visible_width(node), rc.dy = synctex_node_box_visible_height(node) + synctex_node_box_visible_depth(node); rects.Push(rc.Round()); } if (firstpage <= 0) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; return PDFSYNCERR_SUCCESS; }
// see http://itexmac.sourceforge.net/pdfsync.html for the specification int Pdfsync::RebuildIndex() { size_t len; ScopedMem<char> data(file::ReadAll(syncfilepath, &len)); if (!data) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; // convert the file data into a list of zero-terminated strings str::TransChars(data, "\r\n", "\0\0"); // parse preamble (jobname and version marker) char *line = data; char *dataEnd = data + len; // replace star by spaces (TeX uses stars instead of spaces in filenames) str::TransChars(line, "*/", " \\"); ScopedMem<WCHAR> jobName(str::conv::FromAnsi(line)); jobName.Set(str::Join(jobName, L".tex")); jobName.Set(PrependDir(jobName)); line = Advance0Line(line, dataEnd); UINT versionNumber = 0; if (!line || !str::Parse(line, "version %u", &versionNumber) || versionNumber != 1) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; // reset synchronizer database srcfiles.Reset(); lines.Reset(); points.Reset(); fileIndex.Reset(); sheetIndex.Reset(); Vec<size_t> filestack; UINT page = 1; sheetIndex.Append(0); // add the initial tex file to the source file stack filestack.Push(srcfiles.Count()); srcfiles.Append(jobName.StealData()); PdfsyncFileIndex findex = { 0 }; fileIndex.Append(findex); PdfsyncLine psline; PdfsyncPoint pspoint; // parse data UINT maxPageNo = engine->PageCount(); while ((line = Advance0Line(line, dataEnd)) != NULL) { if (!line) break; switch (*line) { case 'l': psline.file = filestack.Last(); if (str::Parse(line, "l %u %u %u", &psline.record, &psline.line, &psline.column)) lines.Append(psline); else if (str::Parse(line, "l %u %u", &psline.record, &psline.line)) { psline.column = 0; lines.Append(psline); } // else dbg("Bad 'l' line in the pdfsync file"); break; case 's': if (str::Parse(line, "s %u", &page)) sheetIndex.Append(points.Count()); // else dbg("Bad 's' line in the pdfsync file"); // if (0 == page || page > maxPageNo) // dbg("'s' line with invalid page number in the pdfsync file"); break; case 'p': pspoint.page = page; if (0 == page || page > maxPageNo) /* ignore point for invalid page number */; else if (str::Parse(line, "p %u %u %u", &pspoint.record, &pspoint.x, &pspoint.y)) points.Append(pspoint); else if (str::Parse(line, "p* %u %u %u", &pspoint.record, &pspoint.x, &pspoint.y)) points.Append(pspoint); // else dbg("Bad 'p' line in the pdfsync file"); break; case '(': { ScopedMem<WCHAR> filename(str::conv::FromAnsi(line + 1)); // if the filename contains quotes then remove them // TODO: this should never happen!? if (filename[0] == '"' && filename[str::Len(filename) - 1] == '"') filename.Set(str::DupN(filename + 1, str::Len(filename) - 2)); // undecorate the filepath: replace * by space and / by \ str::TransChars(filename, L"*/", L" \\"); // if the file name extension is not specified then add the suffix '.tex' if (str::IsEmpty(path::GetExt(filename))) filename.Set(str::Join(filename, L".tex")); // ensure that the path is absolute if (PathIsRelative(filename)) filename.Set(PrependDir(filename)); filestack.Push(srcfiles.Count()); srcfiles.Append(filename.StealData()); findex.start = findex.end = lines.Count(); fileIndex.Append(findex); } break; case ')': if (filestack.Count() > 1) fileIndex.At(filestack.Pop()).end = lines.Count(); // else dbg("Unbalanced ')' line in the pdfsync file"); break; default: // dbg("Ignoring invalid pdfsync line starting with '%c'", *line); break; } } fileIndex.At(0).end = lines.Count(); assert(filestack.Count() == 1); return Synchronizer::RebuildIndex(); }