I know, Python is not C++, but I wanted some exercise in this language.
The specified font size and the area locations do not fit too well, even
if the font size would be scaled down by around 75%. Therefore I have used \resizebox
to force the text to the size of the specified area location.
The word is placed via \put
of the picture
environment. There are more
powerful environments for drawings like TikZ or pstricks
. But advanced features are not needed here and picture
is much more efficient and faster.
The coordinates are transformed, since the vertical axis has the opposite
direction of the y values need for the picture
environment. The program
calculates the minimum and maximum values and sets the lower left corner
as new origin.
Before the word is set, the program sets some lines to show the specified
rectangle.
The Python program can be called with the text file name as argument. Without argument, the text data are taken from the standard input. Output is standard output, which can be redirected to a TeX file.
The font and size settings are optimized, thus that the font is only defined
once. Also the font is only set for a word, if the font has changed.
The generated file is processed with LuaLaTeX or XeLaTeX because of package fontspec
. Then it is easier to use arbitrary font names, as long as LuaTeX or XeTeX know, where to find them.
Some characters need special treatment in TeX, see the program code for an example, how to deal with these characters.
A little more elegant would be to put the TeX preamble into a new LaTeX class
file. Then the generated TeX file would just contain \documentclass{...}
as whole preamble.
#!/usr/bin/env python
import fileinput;
import re;
line_pattern = re.compile(
r"word:\s*'(?P<word>[^']+)';\s*"
r"conf:\s*[0-9\.]+;\s*"
r"pointsize:\s*'(?P<pointsize>\d+)':,"
r"font_name:\s*'(?P<font_name>[^']+)',\s*"
r"X1:\s*'(?P<X1>\d+)',\s*"
r"X2:\s*'(?P<X2>\d+)',\s*"
r"Y1:\s*'(?P<Y1>\d+)',\s*"
r"Y2:\s*'(?P<Y2>\d+)'\s*")
# Mapping from character to TeX representation
tex_char_map = {}
for c in "%#$&":
tex_char_map[c] = "\\" + c
for c in "_^~\\{}":
tex_char_map[c] = r"\symbol{" + str(ord(c)) + "}"
fonts = set()
words = []
x_min = None
x_max = None
y_min = None
y_max = None
for line in fileinput.input():
match = line_pattern.match(line)
if match:
word = match.group("word")
pointsize = match.group("pointsize")
font_name = match.group("font_name")
X1 = int(match.group("X1"))
X2 = int(match.group("X2"))
Y1 = int(match.group("Y1"))
Y2 = int(match.group("Y2"))
fonts.add(font_name)
data = (word, pointsize, font_name, X1, X2, Y1, Y2)
words.append(data)
# assuming X1 <= X2 and Y1 <= Y2
if x_min is None or X1 < x_min:
x_min = X1
if x_max is None or X2 > x_max:
x_max = X2
if y_min is None or Y1 < y_min:
y_min = Y1
if y_max is None or Y2 > y_max:
y_max = Y2
tex_preamble = r"""\NeedsTeXFormat{LaTeX2e}
\documentclass[border=10pt]{standalone}
\usepackage{fontspec}
\newcommand*{\NewFont}[1]{%
\expandafter\newfontfamily\csname#1\endcsname{#1}%
}
\newcommand*{\SetFont}[1]{%
\csname#1\endcsname
\ignorespaces
}
\newdimen\FontSize
\newdimen\FontUnitLength
\setlength{\FontUnitLength}{.75bp}
\newcommand*{\SetSize}[1]{%
\setlength{\FontSize}{#1\FontUnitLength}%
\fontsize{\FontSize}{1.2\FontSize}\selectfont
\ignorespaces
}
%\usepackage{graphicx}
\newcommand*{\Put}{}
\def\Put(#1,#2)(#3,#4)#5{%
\put(#1,#2){\line(1,0){#3}\line(0,1){#4}}%
\put(#1,#2){\line(0,1){#4}}%
\put(#1,#2){\put(0,#4){\line(1,0){#3}}}%
%\put(#1,#2){\makebox(#3,#4)[lb]{\resizebox{#3\unitlength}{!}{#5}}}%
\put(#1,#2){%
\makebox(#3,#4)[lb]{%
\resizebox{#3\unitlength}{#4\unitlength}{%
\raisebox{\depth}{#5}%
}%
}%
}%
%\put(#1,#2){\makebox(#3,#4)[lb]{#5}}%
}
\setlength{\unitlength}{1bp}"""
print(tex_preamble)
for font in fonts:
print(r"\NewFont{" + font + "}")
print(r"\begin{document}")
picture_width = x_max - x_min
picture_height = y_max - y_min
print(r"\begin{picture}(%d,%d)" % (picture_width, picture_height))
current_font = ""
current_pointsize = ""
for word, pointsize, font_name, X1, X2, Y1, Y2 in words:
if font != current_font:
print(r"\SetFont{" + font + "}")
current_font = font
if pointsize != current_pointsize:
print(r"\SetSize{" + pointsize + "}")
current_pointsize = pointsize
w = ""
for c in word:
if c in tex_char_map:
w += tex_char_map[c]
else:
w += c
width = X2 - X1
height = Y2 - Y1
x = X1 - x_min
y = picture_height - (Y2 - y_min)
print(r"\Put(%d,%d)(%d,%d){%s}" % (x, y, width, height, w))
print(r"\end{picture}")
print(r"\end{document}")
The input data (with closing single quote added for the value of Y2
):
word: 'Swine'; conf: 92.03;pointsize:'48':,font_name:'Verdana_Bold',X1:'152', X2:'283', Y1: '166', Y2: '202'
word: 'flu'; conf: 93.43;pointsize:'48':,font_name:'Verdana_Bold',X1:'299', X2:'354', Y1: '164', Y2: '202'
word: 'tightens'; conf: 90.12;pointsize:'48':,font_name:'Verdana_Bold',X1:'370', X2:'551', Y1: '164', Y2: '211'
word: 'its'; conf: 96.04;pointsize:'48':,font_name:'Verdana_Bold',X1:'568', X2:'617', Y1: '166', Y2: '202'
word: 'grip'; conf: 90.41;pointsize:'48':,font_name:'Verdana_Bold',X1:'632', X2:'716', Y1: '166', Y2: '211'
word: 'over'; conf: 94.29;pointsize:'48':,font_name:'Verdana_Bold',X1:'732', X2:'831', Y1: '175', Y2: '202'
word: 'India'; conf: 94.00;pointsize:'48':,font_name:'Verdana_Bold',X1:'848', X2:'953', Y1: '164', Y2: '202'
The generated LaTeX file:
\NeedsTeXFormat{LaTeX2e}
\documentclass[border=10pt]{standalone}
\usepackage{fontspec}
\newcommand*{\NewFont}[1]{%
\expandafter\newfontfamily\csname#1\endcsname{#1}%
}
\newcommand*{\SetFont}[1]{%
\csname#1\endcsname
\ignorespaces
}
\newdimen\FontSize
\newdimen\FontUnitLength
\setlength{\FontUnitLength}{.75bp}
\newcommand*{\SetSize}[1]{%
\setlength{\FontSize}{#1\FontUnitLength}%
\fontsize{\FontSize}{1.2\FontSize}\selectfont
\ignorespaces
}
%\usepackage{graphicx}
\newcommand*{\Put}{}
\def\Put(#1,#2)(#3,#4)#5{%
\put(#1,#2){\line(1,0){#3}\line(0,1){#4}}%
\put(#1,#2){\line(0,1){#4}}%
\put(#1,#2){\put(0,#4){\line(1,0){#3}}}%
%\put(#1,#2){\makebox(#3,#4)[lb]{\resizebox{#3\unitlength}{!}{#5}}}%
\put(#1,#2){%
\makebox(#3,#4)[lb]{%
\resizebox{#3\unitlength}{#4\unitlength}{%
\raisebox{\depth}{#5}%
}%
}%
}%
%\put(#1,#2){\makebox(#3,#4)[lb]{#5}}%
}
\setlength{\unitlength}{1bp}
\NewFont{Verdana_Bold}
\begin{document}
\begin{picture}(801,47)
\SetFont{Verdana_Bold}
\SetSize{48}
\Put(0,9)(131,36){Swine}
\Put(147,9)(55,38){flu}
\Put(218,0)(181,47){tightens}
\Put(416,9)(49,36){its}
\Put(480,0)(84,45){grip}
\Put(580,9)(99,27){over}
\Put(696,9)(105,38){India}
\end{picture}
\end{document}

pointsize:'48'
to turn into (say)\fontsize{48}{72}\selectfont
and you just don't know the LaTeX commands for font stuff?