1 |
|
# |
2 |
|
# Copyright (c) 2002, 2003 Tuomas Lukka, Asko Soukka |
3 |
|
# |
4 |
|
# This file is part of Navidoc. |
5 |
|
# |
6 |
|
# Navidoc is free software; you can redistribute it and/or modify it under |
7 |
|
# the terms of the GNU Lesser General Public License as published by |
8 |
|
# the Free Software Foundation; either version 2 of the License, or |
9 |
|
# (at your option) any later version. |
10 |
|
# |
11 |
|
# Navidoc is distributed in the hope that it will be useful, but WITHOUT |
12 |
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 |
|
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General |
14 |
|
# Public License for more details. |
15 |
|
# |
16 |
|
# You should have received a copy of the GNU Lesser General |
17 |
|
# Public License along with Navidoc; if not, write to the Free |
18 |
|
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
19 |
|
# MA 02111-1307 USA |
20 |
|
# |
21 |
|
|
22 |
|
# $Id$ |
23 |
|
|
24 |
# |
# |
25 |
|
# Written by Tuomas Lukka, Asko Soukka |
26 |
|
# |
27 |
|
|
28 |
|
__docformat__ = 'reStructuredText' |
29 |
|
|
30 |
|
import os, os.path |
31 |
|
|
32 |
|
from navidoc.utils.path import * |
33 |
|
from navidoc.parser import * |
34 |
|
|
35 |
|
import config |
36 |
|
|
37 |
|
dbg = config.dbg.shorthand('mp') |
38 |
|
dbg_fail = config.dbg.shorthand('mp.fail') |
39 |
|
|
40 |
|
class NamedMPElement(NamedElement): |
41 |
|
def repl(self, s): |
42 |
|
s = s.replace("%%", self.var) |
43 |
|
s = s.replace("%name%", self.name) |
44 |
|
if "stereo" in dir(self): |
45 |
|
s = s.replace("%stereo%", self.stereo or "") |
46 |
|
return s |
47 |
|
def draw_code(self): |
48 |
|
s = """ |
49 |
|
drawmeasuredpic(%%); |
50 |
|
""" |
51 |
|
return self.repl(s) |
52 |
|
|
53 |
|
class SimpleElement(NamedMPElement): |
54 |
|
def __init__(self, var, s, list): |
55 |
|
dbg("Simple element: %s %s %s" % (self, s, list)) |
56 |
|
self.var = var or s |
57 |
|
self.name = s |
58 |
|
self.do_contents(list) |
59 |
|
|
60 |
|
def create_uml(diagram, extension='', scale=1.0): |
61 |
|
""" |
62 |
|
""" |
63 |
|
if not os.path.isfile(_slashify(config.mp_directory)+diagram+'.gen.uml'): |
64 |
|
raise MetapostException(_slashify(config.mp_directory)+diagram+'.gen.uml'+' not found') |
65 |
|
if not os.path.isfile(_slashify(config.mp_directory)+diagram+'.gen.mp'): |
66 |
|
raise MetapostException(_slashify(config.mp_directory)+diagram+'.gen.mp'+' not found') |
67 |
|
|
68 |
|
s = open(_slashify(config.mp_directory)+diagram+'.gen.uml').read() |
69 |
|
l = parseIndented(s) |
70 |
|
m = mplist(l) |
71 |
|
|
72 |
|
if len(extension) > 0: extension = '_'+extension |
73 |
|
|
74 |
|
dir = _slashify(config.mp_directory) |
75 |
|
gen1 = open(dir+diagram+extension+'.gen.mp.1', 'w') |
76 |
|
gen1.write(m.setupCode()+'\n') |
77 |
|
gen1.close() |
78 |
|
gen2 = open(dir+diagram+extension+'.gen.mp.2', 'w') |
79 |
|
gen2.write(m.drawCode()+"\n") |
80 |
|
gen2.close() |
81 |
|
gen3 = open(dir+diagram+extension+".gen.mp.3", "w") |
82 |
|
gen3.write(""" |
83 |
|
prologues := 1; |
84 |
|
input general.mp |
85 |
|
input uml.mp |
86 |
|
|
87 |
|
beginfig(1) |
88 |
|
input %(diagram+extension)s.gen.mp.1 |
89 |
|
input %(diagram)s.gen.mp |
90 |
|
input %(diagram+extension)s.gen.mp.2 |
91 |
|
endfig |
92 |
|
end |
93 |
|
""" % locals()) |
94 |
|
gen3.close() |
95 |
|
|
96 |
|
mp2png(diagram+extension, scale) |
97 |
|
|
98 |
|
def mp2png(diagram, scale=1.0): |
99 |
|
""" |
100 |
|
""" |
101 |
|
config.dbg.out('navidoc', 'Compiling diagram '+diagram) |
102 |
|
|
103 |
|
syscmd = 'mpost --file-line-error-style ' \ |
104 |
|
+ '--interaction nonstopmode ' \ |
105 |
|
+ diagram +'.gen.mp.3 >/dev/null' |
106 |
|
|
107 |
|
dbg(syscmd) |
108 |
|
os.system('cd '+config.mp_directory+';'+syscmd) |
109 |
|
|
110 |
|
log = open(_slashify(config.mp_directory)+diagram+'.gen.log').read() |
111 |
|
if log.find("\n"+'!') != -1: |
112 |
|
dbg_fail('Error occurred while processing the diagram. The log is shown below.') |
113 |
|
dbg_fail(log) |
114 |
|
|
115 |
|
links = [] |
116 |
|
log = log.replace("\n",'') # mpost splits lines awkwardly in the log. |
117 |
|
|
118 |
|
bbox = 0 |
119 |
|
scaling = 2 |
120 |
|
list = '' |
121 |
|
|
122 |
|
# grep all linked areas from log file |
123 |
|
for link in re.findall('LINKRECT\((.*?)\)', log): |
124 |
|
els = link.split(',') |
125 |
|
if els[0] == '"bbox"': |
126 |
|
bbox = els |
127 |
|
dbg(bbox) |
128 |
|
|
129 |
|
# scales the bounding box and prepares it for pstopnm |
130 |
|
if (bbox): |
131 |
|
bbox = psbbox(bbox[1:]) |
132 |
|
list = bbox.pstopnm(scaling) |
133 |
|
dbg(bbox) |
134 |
|
|
135 |
|
scaling = int(scaling / scale) |
136 |
|
|
137 |
|
syscmd = ('pstopnm '+' '.join(list)+' <'+_slashify(config.mp_directory)+diagram+'.gen' \ |
138 |
|
+'.mp.1 2>/dev/null | pnmscale -reduce '+scaling+' 2>/dev/null | ' \ |
139 |
|
+'pnmtopng >'+_slashify(config.mp_directory)+diagram+'.gen.png 2>/dev/null') |
140 |
|
|
141 |
|
dbg(syscmd) |
142 |
|
os.system(syscmd) |
143 |
|
|
144 |
|
class psbbox: |
145 |
|
""" |
146 |
|
PostScriptBoundingBox. |
147 |
|
""" |
148 |
|
def __init__(self, els): |
149 |
|
self.x=(roundDown(els[0]), roundUp(els[2])) |
150 |
|
self.y=(roundUp(els[1]), roundDown(els[3])) |
151 |
|
self.scale = 1 # 1 point = 1 pixel |
152 |
|
self.w = self.scale*(self.x[1]-self.x[0]) |
153 |
|
self.h = self.scale*(self.y[0]-self.y[1]) |
154 |
|
def map_point(self, x, y): |
155 |
|
return (self.scale * (x - self.x[0]), |
156 |
|
self.h - self.scale * (y - self.y[1])); # reverse y |
157 |
|
def pstopnm(self, scale): |
158 |
|
dpi = 72.0 |
159 |
|
return ["%s"%r for r in [ "-llx", self.x[0]/dpi, "-lly", self.y[1]/dpi, |
160 |
|
"-urx", self.x[1]/dpi, "-ury", self.y[0]/dpi, |
161 |
|
"-xsize", self.w * scale, "-ysize", self.h * scale, |
162 |
|
"-xborder", 0, "-yborder", 0]] |
163 |
|
|
164 |
|
class MetapostException(Exception): |
165 |
|
def __init__(self, value): |
166 |
|
self.value = value |
167 |
|
def __str__(self): |
168 |
|
return self.value |
169 |
|
|
170 |
|
def roundDown(x): |
171 |
|
return int(float(x) - 5) |
172 |
|
def roundUp(x): |
173 |
|
return int(float(x) + 5) |