Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
G
gajim-plugins
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
gajim
gajim-plugins
Commits
8bff21d5
Commit
8bff21d5
authored
12 years ago
by
Yann Leboulanger
Browse files
Options
Downloads
Patches
Plain Diff
add latex plugin
parent
9536ebb2
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
latex/__init__.py
+1
-0
1 addition, 0 deletions
latex/__init__.py
latex/config_dialog.ui
+89
-0
89 additions, 0 deletions
latex/config_dialog.ui
latex/latex.py
+399
-0
399 additions, 0 deletions
latex/latex.py
latex/manifest.ini
+8
-0
8 additions, 0 deletions
latex/manifest.ini
with
497 additions
and
0 deletions
latex/__init__.py
0 → 100644
+
1
−
0
View file @
8bff21d5
from
latex
import
LatexPlugin
This diff is collapsed.
Click to expand it.
latex/config_dialog.ui
0 → 100644
+
89
−
0
View file @
8bff21d5
<?xml version="1.0"?>
<interface>
<requires
lib=
"gtk+"
version=
"2.16"
/>
<!-- interface-naming-policy toplevel-contextual -->
<object
class=
"GtkTextBuffer"
id=
"textbuffer1"
>
<property
name=
"text"
>
Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.
</property>
</object>
<object
class=
"GtkWindow"
id=
"window1"
>
<child>
<object
class=
"GtkVBox"
id=
"vbox1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"orientation"
>
vertical
</property>
<property
name=
"spacing"
>
6
</property>
<child>
<object
class=
"GtkHBox"
id=
"hbox111"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"border_width"
>
3
</property>
<property
name=
"spacing"
>
6
</property>
<child>
<object
class=
"GtkLabel"
id=
"label1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"xalign"
>
0
</property>
<property
name=
"label"
translatable=
"yes"
>
PNG dpi:
</property>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
False
</property>
<property
name=
"position"
>
0
</property>
</packing>
</child>
<child>
<object
class=
"GtkEntry"
id=
"png_dpi_label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"invisible_char"
>
●
</property>
<signal
name=
"changed"
handler=
"on_png_dpi_label_changed"
/>
</object>
<packing>
<property
name=
"position"
>
1
</property>
</packing>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"position"
>
0
</property>
</packing>
</child>
<child>
<object
class=
"GtkHBox"
id=
"hbox1"
>
<property
name=
"visible"
>
True
</property>
<child>
<placeholder/>
</child>
<child>
<object
class=
"GtkButton"
id=
"test_button"
>
<property
name=
"label"
translatable=
"yes"
>
Test Latex Configuration
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"receives_default"
>
True
</property>
<signal
name=
"clicked"
handler=
"on_test_button_clicked"
/>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"position"
>
1
</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"position"
>
1
</property>
</packing>
</child>
<child>
<object
class=
"GtkLabel"
id=
"result_label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"label"
translatable=
"yes"
>
Result:
</property>
</object>
<packing>
<property
name=
"position"
>
2
</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
This diff is collapsed.
Click to expand it.
latex/latex.py
0 → 100644
+
399
−
0
View file @
8bff21d5
# -*- coding: utf-8 -*-
#
## plugins/latex/latex.py
##
## Copyright (C) 2010-2011 Yves Fischer <yvesf AT xapek.org>
## Copyright (C) 2011 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from
threading
import
Thread
import
os
import
gtk
import
gobject
from
tempfile
import
mkstemp
,
mkdtemp
import
random
from
subprocess
import
Popen
,
PIPE
from
common
import
gajim
from
common
import
helpers
from
plugins
import
GajimPlugin
from
plugins.helpers
import
log
,
log_calls
from
plugins.gui
import
GajimPluginConfigDialog
gtk
.
gdk
.
threads_init
()
# for gtk.gdk.thread_[enter|leave]()
def
latex_template
(
code
):
return
'''
\\
documentclass[12pt]{article}
\\
usepackage[dvips]{graphicx}
\\
usepackage{amsmath}
\\
usepackage{amssymb}
\\
pagestyle{empty}
\\
begin{document}
\\
begin{large}
\\
begin{gather*}
%s
\\
end{gather*}
\\
end{large}
\\
end{document}
'''
%
(
code
)
def
write_latex
(
filename
,
str_
):
texstr
=
latex_template
(
str_
)
file_
=
open
(
filename
,
"
w+
"
)
file_
.
write
(
texstr
)
file_
.
flush
()
file_
.
close
()
def
popen_nt_friendly
(
command
,
directory
):
if
os
.
name
==
'
nt
'
:
# CREATE_NO_WINDOW
return
Popen
(
command
,
creationflags
=
0x08000000
,
cwd
=
directory
,
stdout
=
PIPE
)
else
:
return
Popen
(
command
,
cwd
=
directory
,
stdout
=
PIPE
)
def
try_run
(
argv
,
directory
):
try
:
p
=
popen_nt_friendly
(
argv
,
directory
)
out
=
p
.
communicate
()[
0
]
log
.
info
(
out
)
return
p
.
wait
()
except
Exception
,
e
:
return
_
(
'
Error executing
"
%(command)s
"
: %(error)s
'
)
%
{
'
command
'
:
"
"
.
join
(
argv
),
'
error
'
:
helpers
.
decode_string
(
str
(
e
))}
BLACKLIST
=
[
'
\def
'
,
'
\\
let
'
,
'
\\
futurelet
'
,
'
\\
newcommand
'
,
'
\\
renewcomment
'
,
'
\\
else
'
,
'
\\
fi
'
,
'
\\
write
'
,
'
\\
input
'
,
'
\\
include
'
,
'
\\
chardef
'
,
'
\\
catcode
'
,
'
\\
makeatletter
'
,
'
\\
noexpand
'
,
'
\\
toksdef
'
,
'
\\
every
'
,
'
\\
errhelp
'
,
'
\\
errorstopmode
'
,
'
\\
scrollmode
'
,
'
\\
nonstopmode
'
,
'
\\
batchmode
'
,
'
\\
read
'
,
'
\\
csname
'
,
'
\\
newhelp
'
,
'
\\
relax
'
,
'
\\
afterground
'
,
'
\\
afterassignment
'
,
'
\\
expandafter
'
,
'
\\
noexpand
'
,
'
\\
special
'
,
'
\\
command
'
,
'
\\
loop
'
,
'
\\
repeat
'
,
'
\\
toks
'
,
'
\\
output
'
,
'
\\
line
'
,
'
\\
mathcode
'
,
'
\\
name
'
,
'
\\
item
'
,
'
\\
section
'
,
'
\\
mbox
'
,
'
\\
DeclareRobustCommand
'
,
'
\\
[
'
,
'
\\
]
'
]
class
LatexRenderer
(
Thread
):
def
__init__
(
self
,
iter_start
,
iter_end
,
buffer_
,
widget
,
png_dpi
):
Thread
.
__init__
(
self
)
self
.
code
=
iter_start
.
get_text
(
iter_end
)
self
.
mark_name
=
'
LatexRendererMark%s
'
%
str
(
random
.
randint
(
0
,
1000
))
self
.
mark
=
buffer_
.
create_mark
(
self
.
mark_name
,
iter_start
,
True
)
self
.
buffer_
=
buffer_
self
.
widget
=
widget
self
.
png_dpi
=
png_dpi
# delete code and show message 'processing'
self
.
buffer_
.
delete
(
iter_start
,
iter_end
)
# iter_start.forward_char()
self
.
buffer_
.
insert
(
iter_start
,
_
(
'
Processing LaTeX
'
))
self
.
start
()
# start background processing
def
run
(
self
):
try
:
if
self
.
check_code
():
self
.
show_image
()
else
:
self
.
show_error
(
_
(
'
There are bad commands!
'
))
except
:
pass
finally
:
self
.
buffer_
.
delete_mark
(
self
.
mark
)
def
show_error
(
self
,
message
):
"""
String -> TextBuffer
"""
gtk
.
gdk
.
threads_enter
()
iter_mark
=
self
.
buffer_
.
get_iter_at_mark
(
self
.
mark
)
iter_end
=
iter_mark
.
copy
().
forward_search
(
_
(
'
Processing LaTeX
'
),
gtk
.
TEXT_SEARCH_TEXT_ONLY
)[
1
]
self
.
buffer_
.
delete
(
iter_mark
,
iter_end
)
pixbuf
=
self
.
widget
.
render_icon
(
gtk
.
STOCK_STOP
,
gtk
.
ICON_SIZE_BUTTON
)
self
.
buffer_
.
insert_pixbuf
(
iter_end
,
pixbuf
)
self
.
buffer_
.
insert
(
iter_end
,
message
)
gtk
.
gdk
.
threads_leave
()
@log_calls
(
'
LatexRenderer
'
)
def
show_image
(
self
):
"""
Latex -> PNG -> TextBuffer
"""
def
fg_str
(
fmt
):
try
:
return
[{
'
hex
'
:
'
+level-colors
'
,
'
tex
'
:
'
-fg
'
}[
fmt
],
gajim
.
interface
.
get_fg_color
(
fmt
)]
except
KeyError
:
# interface may not be available when we test latex at startup
return
[]
except
AttributeError
:
# interface may not be available when we test latext at startup
return
{
'
hex
'
:
[
'
+level-colors
'
,
'
0x000000
'
],
'
tex
'
:
[
'
-fg
'
,
'
rgb 0.0 0.0 0.0
'
]}[
fmt
]
try
:
tmpdir
=
mkdtemp
(
prefix
=
'
gajim_tex
'
)
tmppng
=
mkstemp
(
prefix
=
'
gajim_tex
'
,
suffix
=
'
.png
'
)[
1
]
except
Exception
:
msg
=
'
Could not create temporary files for Latex plugin
'
log
.
debug
(
msg
)
self
.
show_error
(
_
(
'
latex error: %s
\n
===ORIGINAL CODE====
\n
%s
'
)
%
(
msg
,
self
.
code
[
2
:
len
(
self
.
code
)
-
2
]))
return
False
tmpfile
=
os
.
path
.
join
(
tmpdir
,
'
gajim_tex
'
)
# build latex string
write_latex
(
tmpfile
+
'
.tex
'
,
self
.
code
[
2
:
len
(
self
.
code
)
-
2
])
# convert TeX to dvi
exitcode
=
try_run
([
'
latex
'
,
'
--interaction=nonstopmode
'
,
tmpfile
+
'
.tex
'
],
tmpdir
)
if
exitcode
==
0
:
# convert dvi to png
log
.
debug
(
'
DVI OK
'
)
exitcode
=
try_run
([
'
dvipng
'
]
+
fg_str
(
'
tex
'
)
+
[
'
-T
'
,
'
tight
'
,
'
-D
'
,
self
.
png_dpi
,
tmpfile
+
'
.dvi
'
,
'
-o
'
,
tmpfile
+
'
.png
'
],
tmpdir
)
if
exitcode
:
# dvipng failed, try convert
log
.
debug
(
'
dvipng failed, try convert
'
)
exitcode
=
try_run
([
'
convert
'
]
+
fg_str
(
'
hex
'
)
+
[
'
-trim
'
,
'
-density
'
,
self
.
png_dpi
,
tmpfile
+
'
.dvi
'
,
tmpfile
+
'
.png
'
],
tmpdir
)
# remove temp files created by us and TeX
extensions
=
[
'
.tex
'
,
'
.log
'
,
'
.aux
'
,
'
.dvi
'
]
for
ext
in
extensions
:
try
:
os
.
remove
(
tmpfile
+
ext
)
except
Exception
:
pass
if
exitcode
==
0
:
log
.
debug
(
'
PNG OK
'
)
os
.
rename
(
tmpfile
+
'
.png
'
,
tmppng
)
else
:
log
.
debug
(
'
PNG FAILED
'
)
os
.
remove
(
tmppng
)
os
.
rmdir
(
tmpdir
)
self
.
show_error
(
_
(
'
Convertion to image failed
\n
===ORIGINAL CODE====
'
'
\n
%s
'
)
%
self
.
code
[
2
:
len
(
self
.
code
)
-
2
])
return
False
log
.
debug
(
'
Loading PNG %s
'
%
tmppng
)
try
:
gtk
.
gdk
.
threads_enter
()
pixbuf
=
gtk
.
gdk
.
pixbuf_new_from_file
(
tmppng
)
log
.
debug
(
'
png loaded
'
)
iter_mark
=
self
.
buffer_
.
get_iter_at_mark
(
self
.
mark
)
iter_end
=
iter_mark
.
copy
().
forward_search
(
'
Processing LaTeX
'
,
gtk
.
TEXT_SEARCH_TEXT_ONLY
)[
1
]
log
.
debug
(
'
Delete old Text
'
)
self
.
buffer_
.
delete
(
iter_mark
,
iter_end
)
log
.
debug
(
'
Insert pixbuf
'
)
self
.
buffer_
.
insert_pixbuf
(
iter_end
,
pixbuf
)
except
gobject
.
GError
:
self
.
show_error
(
_
(
'
Cannot open %s for reading
'
)
%
tmppng
)
log
.
debug
(
'
Cant open %s for reading
'
%
tmppng
)
finally
:
gtk
.
gdk
.
threads_leave
()
os
.
remove
(
tmppng
)
def
check_code
(
self
):
for
bad_cmd
in
BLACKLIST
:
if
self
.
code
.
find
(
bad_cmd
)
!=
-
1
:
# Found bad command
return
False
return
True
class
LatexPluginConfiguration
(
GajimPluginConfigDialog
):
def
init
(
self
):
self
.
GTK_BUILDER_FILE_PATH
=
self
.
plugin
.
local_file_path
(
'
config_dialog.ui
'
)
self
.
xml
=
gtk
.
Builder
()
self
.
xml
.
set_translation_domain
(
'
gajim_plugins
'
)
self
.
xml
.
add_objects_from_file
(
self
.
GTK_BUILDER_FILE_PATH
,
[
'
vbox1
'
])
hbox
=
self
.
xml
.
get_object
(
'
vbox1
'
)
self
.
child
.
pack_start
(
hbox
)
self
.
result_label
=
self
.
xml
.
get_object
(
'
result_label
'
)
self
.
xml
.
connect_signals
(
self
)
def
on_run
(
self
):
widget
=
self
.
xml
.
get_object
(
'
png_dpi_label
'
)
widget
.
set_text
(
str
(
self
.
plugin
.
config
[
'
png_dpi
'
]))
def
show_result
(
self
,
msg
):
self
.
result_label
.
set_text
(
self
.
result_label
.
get_text
()
+
'
\n
'
+
msg
)
def
on_test_button_clicked
(
self
,
widget
):
"""
performs very simple checks (check if executable is in PATH)
"""
self
.
show_result
(
_
(
'
Test Latex Binary
'
))
exitcode
=
try_run
([
'
latex
'
,
'
-version
'
],
None
)
if
exitcode
!=
0
:
self
.
show_result
(
_
(
'
No LaTeX binary found in PATH
'
))
else
:
self
.
show_result
(
_
(
'
OK
'
))
self
.
show_result
(
_
(
'
Test dvipng
'
))
exitcode
=
try_run
([
'
dvipng
'
,
'
--version
'
],
None
)
if
exitcode
!=
0
:
self
.
show_result
(
_
(
'
No dvipng binary found in PATH
'
))
else
:
self
.
show_result
(
_
(
'
OK
'
))
self
.
show_result
(
_
(
'
Test ImageMagick
'
))
exitcode
=
try_run
([
'
convert
'
,
'
-version
'
],
None
)
if
exitcode
!=
0
:
self
.
show_result
(
_
(
'
No convert binary found in PATH
'
))
else
:
self
.
show_result
(
_
(
'
OK
'
))
def
on_png_dpi_label_changed
(
self
,
label
):
self
.
plugin
.
config
[
'
png_dpi
'
]
=
label
.
get_text
()
class
LatexPlugin
(
GajimPlugin
):
def
init
(
self
):
self
.
description
=
_
(
'
Invoke Latex to render $$foobar$$ sourrounded
'
\
'
Latex equations. Needs latex and dvipng or ImageMagick.
'
)
self
.
config_dialog
=
LatexPluginConfiguration
(
self
)
self
.
config_default_values
=
{
'
png_dpi
'
:
(
'
108
'
,
''
)}
self
.
gui_extension_points
=
{
'
chat_control_base
'
:
(
self
.
connect_with_chat_control_base
,
self
.
disconnect_from_chat_control_base
)
}
self
.
test_activatable
()
def
test_activatable
(
self
):
"""
performs very simple checks (check if executable is in PATH)
"""
self
.
available_text
=
''
exitcode
=
try_run
([
'
latex
'
,
'
-version
'
],
None
)
if
exitcode
!=
0
:
latex_available
=
False
else
:
latex_available
=
True
exitcode
=
try_run
([
'
dvipng
'
,
'
--version
'
],
None
)
if
exitcode
!=
0
:
dvipng_available
=
False
else
:
dvipng_available
=
True
exitcode
=
try_run
([
'
convert
'
,
'
-version
'
],
None
)
if
exitcode
!=
0
:
imagemagick_available
=
False
else
:
imagemagick_available
=
True
pkgs
=
''
if
not
latex_available
:
if
os
.
name
==
'
nt
'
:
pkgs
=
'
MikTex
'
else
:
pkgs
=
'
texlive-latex-base
'
self
.
available_text
=
_
(
'
LaTeX is not available
'
)
self
.
activatable
=
False
if
not
dvipng_available
and
not
imagemagick_available
:
if
os
.
name
==
'
nt
'
:
if
not
pkgs
:
pkgs
=
'
MikTex
'
else
:
if
pkgs
:
pkgs
+=
_
(
'
and
'
)
pkgs
+=
'
%s or %s
'
%
(
'
dvipng
'
,
'
ImageMagick
'
)
if
self
.
available_text
:
self
.
available_text
+=
'
and
'
self
.
available_text
+=
_
(
'
dvipng and Imagemagick are not available
'
)
if
self
.
available_text
:
self
.
activatable
=
False
self
.
available_text
+=
_
(
'
. Install %s
'
)
%
pkgs
def
textview_event_after
(
self
,
tag
,
widget
,
event
,
iter
):
"""
start rendering if clicked on a link
"""
if
tag
.
get_property
(
'
name
'
)
!=
'
latex
'
or
\
event
.
type
!=
gtk
.
gdk
.
BUTTON_PRESS
:
return
dollar_start
,
iter_start
=
iter
.
backward_search
(
'
$$
'
,
gtk
.
TEXT_SEARCH_TEXT_ONLY
)
iter_end
,
dollar_end
=
iter
.
forward_search
(
'
$$
'
,
gtk
.
TEXT_SEARCH_TEXT_ONLY
)
LatexRenderer
(
dollar_start
,
dollar_end
,
widget
.
get_buffer
(),
widget
,
self
.
config
[
'
png_dpi
'
])
def
textbuffer_live_latex_expander
(
self
,
tb
):
"""
called when conversation text widget changes
"""
def
split_list
(
list
):
newlist
=
[]
for
i
in
range
(
0
,
len
(
list
)
-
1
,
2
):
newlist
.
append
(
[
list
[
i
],
list
[
i
+
1
],
]
)
return
newlist
assert
isinstance
(
tb
,
gtk
.
TextBuffer
)
start_iter
=
tb
.
get_start_iter
()
points
=
[]
tuple_found
=
start_iter
.
forward_search
(
'
$$
'
,
gtk
.
TEXT_SEARCH_TEXT_ONLY
)
while
tuple_found
!=
None
:
points
.
append
(
tuple_found
)
tuple_found
=
tuple_found
[
1
].
forward_search
(
'
$$
'
,
gtk
.
TEXT_SEARCH_TEXT_ONLY
)
for
pair
in
split_list
(
points
):
tb
.
apply_tag_by_name
(
'
latex
'
,
pair
[
0
][
1
],
pair
[
1
][
0
])
def
connect_with_chat_control_base
(
self
,
chat_control
):
d
=
{}
tv
=
chat_control
.
conv_textview
.
tv
tb
=
tv
.
get_buffer
()
self
.
latex_tag
=
gtk
.
TextTag
(
'
latex
'
)
self
.
latex_tag
.
set_property
(
'
foreground
'
,
'
blue
'
)
self
.
latex_tag
.
set_property
(
'
underline
'
,
'
single
'
)
d
[
'
tag_id
'
]
=
self
.
latex_tag
.
connect
(
'
event
'
,
self
.
textview_event_after
)
tb
.
get_tag_table
().
add
(
self
.
latex_tag
)
d
[
'
h_id
'
]
=
tb
.
connect
(
'
changed
'
,
self
.
textbuffer_live_latex_expander
)
chat_control
.
latexs_expander_plugin_data
=
d
return
True
def
disconnect_from_chat_control_base
(
self
,
chat_control
):
d
=
chat_control
.
latexs_expander_plugin_data
tv
=
chat_control
.
conv_textview
.
tv
tv
.
get_buffer
().
disconnect
(
d
[
'
h_id
'
])
self
.
latex_tag
.
disconnect
(
d
[
'
tag_id
'
])
This diff is collapsed.
Click to expand it.
latex/manifest.ini
0 → 100644
+
8
−
0
View file @
8bff21d5
[info]
name:
Latex
short_name:
latex
version:
0.1
description:
render
received
latex
code
authors:
Yves
Fischer
<yvesf@xapek.org>
Yann
Leboulanger
<asterix@lagaule.org>
homepage:
http://www.gajim.org/
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment