/** * ev_document_synctex_backward_search: * @document: a #EvDocument * @page_index: the target page * @x: X coordinate * @y: Y coordinate * * Peforms a Synctex backward search to obtain the TeX input file, line and * (possibly) column corresponding to the position (@x,@y) (in 72dpi * coordinates) in the @page of @document. * * Returns: A pointer to the EvSourceLink structure that holds the result. @NULL if synctex * is not enabled for the document or no result is found. * The EvSourceLink pointer should be freed with g_free after it is used. */ EvSourceLink * ev_document_synctex_backward_search (EvDocument *document, gint page_index, gfloat x, gfloat y) { EvSourceLink *result = NULL; synctex_scanner_t scanner; g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL); scanner = document->priv->synctex_scanner; if (!scanner) return NULL; if (synctex_edit_query (scanner, page_index + 1, x, y) > 0) { synctex_node_t node; /* We assume that a backward search returns either zero or one result_node */ node = synctex_next_result (scanner); if (node != NULL) { const gchar *filename; filename = synctex_scanner_get_name (scanner, synctex_node_tag (node)); if (filename) { result = ev_source_link_new (filename, synctex_node_line (node), synctex_node_column (node)); } } } return result; }
/** * ev_document_synctex_forward_search: * @document: a #EvDocument * @source_link: a #EvSourceLink * * Peforms a Synctex forward search to obtain the area in the document * corresponding to the position @line and @column number in the source Tex file * * Returns: An EvMapping with the page number and area corresponfing to * the given line in the source file. It must be free with g_free when done */ EvMapping * ev_document_synctex_forward_search (EvDocument *document, EvSourceLink *link) { EvMapping *result = NULL; synctex_scanner_t scanner; g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL); scanner = document->priv->synctex_scanner; if (!scanner) return NULL; if (synctex_display_query (scanner, link->filename, link->line, link->col) > 0) { synctex_node_t node; gint page; if ((node = synctex_next_result (scanner))) { result = g_new (EvMapping, 1); page = synctex_node_page (node) - 1; result->data = GINT_TO_POINTER (page); result->area.x1 = synctex_node_box_visible_h (node); result->area.y1 = synctex_node_box_visible_v (node) - synctex_node_box_visible_height (node); result->area.x2 = synctex_node_box_visible_width (node) + result->area.x1; result->area.y2 = synctex_node_box_visible_depth (node) + synctex_node_box_visible_height (node) + result->area.y1; } } return result; }
void SynctexHandler::synctexClick(const QPointF &pagePos, int pageNumber) { if (!m_synctexScanner) return; if (synctex_edit_query(m_synctexScanner, pageNumber + 1, pagePos.x(), pagePos.y()) > 0) { synctex_node_t synctexNode; while ((synctexNode = synctex_next_result(m_synctexScanner)) != 0) { const QString texFileName = QString::fromUtf8(synctex_scanner_get_name(m_synctexScanner, synctex_node_tag(synctexNode))); const QDir currentDir(QFileInfo(m_fileName).canonicalPath()); Q_EMIT openTexDocument(QFileInfo(currentDir, texFileName).canonicalFilePath(), synctex_node_line(synctexNode)); } } }
QList<SynctexTextBox> SynctexHandler::syncFromSource(const QString &sourceFile, int lineNumber) { QList<SynctexTextBox> textBoxList; if (!m_synctexScanner) return textBoxList; // find the name synctex is using for this file const QFileInfo sourceFileInfo(sourceFile); const QDir currentDir(QFileInfo(m_fileName).canonicalPath()); synctex_node_t synctexNode = synctex_scanner_input(m_synctexScanner); QString texFileName; bool found = false; while (synctexNode) { texFileName = QString::fromUtf8(synctex_scanner_get_name(m_synctexScanner, synctex_node_tag(synctexNode))); if (QFileInfo(currentDir, texFileName) == sourceFileInfo) { found = true; break; } synctexNode = synctex_node_sibling(synctexNode); } if (!found) return textBoxList; if (synctex_display_query(m_synctexScanner, texFileName.toUtf8().data(), lineNumber, 0) > 0) { int pageNumber = -1; while ((synctexNode = synctex_next_result(m_synctexScanner)) != 0) { if (pageNumber < 0) pageNumber = synctex_node_page(synctexNode); if (synctex_node_page(synctexNode) != pageNumber) continue; const QRectF textBox(synctex_node_box_visible_h(synctexNode), synctex_node_box_visible_v(synctexNode) - synctex_node_box_visible_height(synctexNode), synctex_node_box_visible_width(synctexNode), synctex_node_box_visible_height(synctexNode)); SynctexTextBox synctexTextBox; synctexTextBox.pageNumber = pageNumber; synctexTextBox.textBox = textBox; textBoxList << synctexTextBox; } } return textBoxList; }
bool synctex_get_input_line_column(const char* filename, unsigned int page, int x, int y, char** input_file, unsigned int* line, unsigned int* column) { if (filename == NULL) { return false; } synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1); if (scanner == NULL) { girara_debug("Failed to create synctex scanner."); return false; } synctex_scanner_t temp = synctex_scanner_parse(scanner); if (temp == NULL) { girara_debug("Failed to parse synctex file."); synctex_scanner_free(scanner); return false; } bool ret = false; if (synctex_edit_query(scanner, page + 1u, x, y) > 0) { /* Assume that a backward search returns at most one result. */ synctex_node_t node = synctex_next_result(scanner); if (node != NULL) { if (input_file != NULL) { *input_file = g_strdup(synctex_scanner_get_name(scanner, synctex_node_tag(node))); } if (line != NULL) { *line = synctex_node_line(node); } if (column != NULL) { *column = synctex_node_column(node); } ret = true; } } synctex_scanner_free(scanner); return ret; }
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; }
int synctex_edit_proceed(synctex_edit_params_t * Ps) { synctex_scanner_t scanner = NULL; #if SYNCTEX_DEBUG printf("page:%i\n",Ps->page); printf("x:%f\n",Ps->x); printf("y:%f\n",Ps->y); printf("almost output:%s\n",Ps->output); printf("editor:%s\n",Ps->editor); printf("offset:%i\n",Ps->offset); printf("context:%s\n",Ps->context); printf("cwd:%s\n",getcwd(NULL,0)); #endif scanner = synctex_scanner_new_with_output_file(Ps->output,Ps->directory,1); if(NULL == scanner) { synctex_help_edit("No SyncTeX available for %s",Ps->output); return -1; } if(synctex_edit_query(scanner,Ps->page,Ps->x,Ps->y)) { synctex_node_t node = NULL; const char * input = NULL; if(NULL != (node = synctex_next_result(scanner)) && NULL != (input = synctex_scanner_get_name(scanner,synctex_node_tag(node)))) { /* filtering the command */ if(Ps->editor && strlen(Ps->editor)) { size_t size = 0; char * where = NULL; char * buffer = NULL; char * buffer_cur = NULL; int printed; int status; size = strlen(Ps->editor)+3*sizeof(int)+3*SYNCTEX_STR_SIZE; buffer = malloc(size+1); if(NULL == buffer) { printf("SyncTeX ERROR: No memory available\n"); return -1; } buffer[size]='\0'; /* Replace %{ by &{, then remove all unescaped '%'*/ while((where = strstr(Ps->editor,"%{")) != NULL) { *where = '&'; } where = Ps->editor; while(where &&(where = strstr(where,"%"))) { if(strlen(++where)) { if(*where == '%') { ++where; } else { *(where-1)='&'; } } } buffer_cur = buffer; /* find the next occurrence of a format key */ where = Ps->editor; while(Ps->editor && (where = strstr(Ps->editor,"&{"))) { #define TEST(KEY,FORMAT,WHAT)\ if(!strncmp(where,KEY,strlen(KEY))) {\ printed = where-Ps->editor;\ if(buffer_cur != memcpy(buffer_cur,Ps->editor,(size_t)printed)) {\ synctex_help_edit("Memory copy problem");\ free(buffer);\ return -1;\ }\ buffer_cur += printed;size-=printed;\ printed = snprintf(buffer_cur,size,FORMAT,WHAT);\ if((unsigned)printed >= (unsigned)size) {\ synctex_help_edit("Snprintf problem");\ free(buffer);\ return -1;\ }\ buffer_cur += printed;size-=printed;\ *buffer_cur='\0';\ Ps->editor = where+strlen(KEY);\ continue;\ } TEST("&{output}", "%s",Ps->output); TEST("&{input}", "%s",input); TEST("&{line}", "%i",synctex_node_line(node)); TEST("&{column}", "%i",-1); TEST("&{offset}", "%i",Ps->offset); TEST("&{context}","%s",Ps->context); #undef TEST break; } /* copy the rest of editor into the buffer */ if(buffer_cur != memcpy(buffer_cur,Ps->editor,strlen(Ps->editor))) { fputs("! synctex_edit: Memory copy problem",stderr); free(buffer); return -1; }\ printf("SyncTeX: Executing\n%s\n",buffer); status = system(buffer); free(buffer); buffer = NULL; return status; } else { /* just print out the results */ puts("SyncTeX result begin"); do { printf( "Output:%s\n" "Input:%s\n" "Line:%i\n" "Column:%i\n" "Offset:%i\n" "Context:%s\n", Ps->output, input, synctex_node_line(node), synctex_node_column(node), Ps->offset, (Ps->context?Ps->context:"")); } while((node = synctex_next_result(scanner)) != NULL); puts("SyncTeX result end"); } } } return 0; }
int synctex_view_proceed(synctex_view_params_t * Ps) { synctex_scanner_t scanner = NULL; size_t size = 0; #if SYNCTEX_DEBUG printf("line:%i\n",Ps->line); printf("column:%i\n",Ps->column); printf("input:%s\n",Ps->input); printf("viewer:%s\n",Ps->viewer); printf("before:%s\n",Ps->before); printf("offset:%i\n",Ps->offset); printf("middle:%s\n",Ps->middle); printf("after:%s\n",Ps->after); printf("output:%s\n",Ps->output); printf("cwd:%s\n",getcwd(NULL,0)); #endif /* We assume that viewer is not so big: */ # define SYNCTEX_STR_SIZE 65536 if(Ps->viewer && strlen(Ps->viewer)>=SYNCTEX_STR_SIZE) { synctex_help_view("Viewer command is too long"); return -1; } scanner = synctex_scanner_new_with_output_file(Ps->output,Ps->directory,1); if(scanner && synctex_display_query(scanner,Ps->input,Ps->line,Ps->column)) { synctex_node_t node = NULL; if((node = synctex_next_result(scanner)) != NULL) { /* filtering the command */ if(Ps->viewer && strlen(Ps->viewer)) { char * viewer = Ps->viewer; char * where = NULL; char * buffer = NULL; char * buffer_cur = NULL; int printed = 0; int status = 0; /* Preparing the buffer where everything will be printed */ size = strlen(viewer)+3*sizeof(int)+6*sizeof(float)+4*(SYNCTEX_STR_SIZE); buffer = malloc(size+1); if(NULL == buffer) { synctex_help_view("No memory available"); return -1; } /* Properly terminate the buffer, no bad access for string related functions. */ buffer[size] = '\0'; /* Replace %{ by &{, then remove all unescaped '%'*/ while((where = strstr(viewer,"%{")) != NULL) { *where = '&'; } /* find all the unescaped '%', change to a safe character */ where = viewer; while(where && (where = strstr(where,"%"))) { /* Find the next occurrence of a "%", * if it is not followed by another "%", * replace it by a "&" */ if(strlen(++where)) { if(*where == '%') { ++where; } else { *(where-1)='&'; } } } buffer_cur = buffer; /* find the next occurrence of a format key */ where = viewer; while(viewer && (where = strstr(viewer,"&{"))) { #define TEST(KEY,FORMAT,WHAT)\ if(!strncmp(where,KEY,strlen(KEY))) {\ printed = where-viewer;\ if(buffer_cur != memcpy(buffer_cur,viewer,(size_t)printed)) {\ synctex_help_view("Memory copy problem");\ free(buffer);\ return -1;\ }\ buffer_cur += printed;size-=printed;\ printed = snprintf(buffer_cur,size,FORMAT,WHAT);\ if((unsigned)printed >= (unsigned)size) {\ synctex_help_view("Snprintf problem");\ free(buffer);\ return -1;\ }\ buffer_cur += printed;size-=printed;\ *buffer_cur='\0';\ viewer = where+strlen(KEY);\ continue;\ } TEST("&{output}","%s",synctex_scanner_get_output(scanner)); TEST("&{page}", "%i",synctex_node_page(node)-1); TEST("&{page+1}","%i",synctex_node_page(node)); TEST("&{x}", "%f",synctex_node_visible_h(node)); TEST("&{y}", "%f",synctex_node_visible_v(node)); TEST("&{h}", "%f",synctex_node_box_visible_h(node)); TEST("&{v}", "%f",synctex_node_box_visible_v(node)+synctex_node_box_visible_depth(node)); TEST("&{width}", "%f",fabs(synctex_node_box_visible_width(node))); TEST("&{height}","%f",fmax(synctex_node_box_visible_height(node)+synctex_node_box_visible_depth(node),1)); TEST("&{before}","%s",(Ps->before && strlen(Ps->before)<SYNCTEX_STR_SIZE?Ps->before:"")); TEST("&{offset}","%i",Ps->offset); TEST("&{middle}","%s",(Ps->middle && strlen(Ps->middle)<SYNCTEX_STR_SIZE?Ps->middle:"")); TEST("&{after}", "%s",(Ps->after && strlen(Ps->after)<SYNCTEX_STR_SIZE?Ps->after:"")); #undef TEST break; } /* copy the rest of viewer into the buffer */ if(buffer_cur != strncpy(buffer_cur,viewer,size + 1)) { synctex_help_view("Memory copy problem"); free(buffer); return -1; } buffer_cur[size] = '\0'; printf("SyncTeX: Executing\n%s\n",buffer); status = system(buffer); free(buffer); buffer = NULL; return status; } else { /* just print out the results */ puts("SyncTeX result begin"); do { printf( "Output:%s\n" "Page:%i\n" "x:%f\n" "y:%f\n" "h:%f\n" "v:%f\n" "W:%f\n" "H:%f\n" "before:%s\n" "offset:%i\n" "middle:%s\n" "after:%s\n", Ps->output, synctex_node_page(node), synctex_node_visible_h(node), synctex_node_visible_v(node), synctex_node_box_visible_h(node), synctex_node_box_visible_v(node)+synctex_node_box_visible_depth(node), synctex_node_box_visible_width(node), synctex_node_box_visible_height(node)+synctex_node_box_visible_depth(node), (Ps->before?Ps->before:""), Ps->offset, (Ps->middle?Ps->middle:""), (Ps->after?Ps->after:"")); } while((node = synctex_next_result(scanner)) != NULL); puts("SyncTeX result end"); } } } return 0; }
girara_list_t* synctex_rectangles_from_position(const char* filename, const char* input_file, int line, int column, unsigned int* page, girara_list_t** secondary_rects) { if (filename == NULL || input_file == NULL || page == NULL) { return NULL; } synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1); if (scanner == NULL) { girara_debug("Failed to create synctex scanner."); return NULL; } synctex_scanner_t temp = synctex_scanner_parse(scanner); if (temp == NULL) { girara_debug("Failed to parse synctex file."); synctex_scanner_free(scanner); return NULL; } girara_list_t* hitlist = girara_list_new2(g_free); girara_list_t* other_rects = girara_list_new2(g_free); if (synctex_display_query(scanner, input_file, line, column) > 0) { synctex_node_t node = NULL; bool got_page = false; while ((node = synctex_next_result (scanner)) != NULL) { const unsigned int current_page = synctex_node_page(node) - 1; if (got_page == false) { got_page = true; *page = current_page; } zathura_rectangle_t rect = { 0, 0, 0, 0 }; rect.x1 = synctex_node_box_visible_h(node); rect.y1 = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node); rect.x2 = rect.x1 + synctex_node_box_visible_width(node); rect.y2 = synctex_node_box_visible_depth(node) + synctex_node_box_visible_height (node) + rect.y1; if (*page == current_page) { zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t)); if (real_rect == NULL) { continue; } *real_rect = rect; girara_list_append(hitlist, real_rect); } else { synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t)); if (page_rect == NULL) { continue; } page_rect->page = current_page; page_rect->rect = rect; girara_list_append(other_rects, page_rect); } } } synctex_scanner_free(scanner); if (secondary_rects != NULL) { *secondary_rects = other_rects; } else { girara_list_free(other_rects); } return hitlist; }
void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y) { if (zathura == NULL || page == NULL || zathura->synctex.editor == NULL) { return; } zathura_document_t* document = zathura_page_get_document(page); if (document == NULL) { return; } const char *filename = zathura_document_get_path(document); if (filename == NULL) { return; } synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1); if (scanner == NULL) { girara_debug("Failed to create synctex scanner."); return; } synctex_scanner_t temp = synctex_scanner_parse(scanner); if (temp == NULL) { girara_debug("Failed to parse synctex file."); synctex_scanner_free(scanner); return; } if (synctex_edit_query(scanner, zathura_page_get_index(page) + 1, x, y) > 0) { /* Assume that a backward search returns either at most one result. */ synctex_node_t node = synctex_next_result(scanner); if (node != NULL) { const char* input_file = synctex_scanner_get_name(scanner, synctex_node_tag(node)); const int line = synctex_node_line(node); const int column = synctex_node_column (node); char* linestr = g_strdup_printf("%d", line); char* columnstr = g_strdup_printf("%d", column); gchar** argv = NULL; gint argc = 0; if (g_shell_parse_argv(zathura->synctex.editor, &argc, &argv, NULL) == TRUE) { for (gint i = 0; i != argc; ++i) { char* temp = girara_replace_substring(argv[i], "%{line}", linestr); g_free(argv[i]); argv[i] = temp; temp = girara_replace_substring(argv[i], "%{column}", columnstr); g_free(argv[i]); argv[i] = temp; temp = girara_replace_substring(argv[i], "%{input}", input_file); g_free(argv[i]); argv[i] = temp; } g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); g_strfreev(argv); } g_free(linestr); g_free(columnstr); } } synctex_scanner_free(scanner); }