3 Let's write our own python parser to clean up the pdf (after
5 Instructions: run pdftotext like this:
7 $ pdftotext -f 9 -l 81 -raw -nopgbrk 04_06PU.PDF 04_06PU-3.txt
9 then run the python parser like this:
11 $ python ParseDict.py 04_06PU.txt dicomV3.dic
16 PdfTextParser takes as input a text file (produced by pdftotext)
17 and create as output a clean file (ready to be processed) by
19 Warning: PdfTextParser does not expand:
20 - (xxxx,xxxx to xxxx) xxxxxxxxxxxx
22 - (12xx, 3456) comment...
28 self._InputFilename = ''
29 self._OutputFilename = ''
32 self._PreviousBuffers = []
34 def SetInputFileName(self,s):
35 self._InputFilename = s
37 def SetOutputFileName(self,s):
38 self._OutputFilename = s
40 # Function returning if s is a comment for sure
41 def IsAComment(self,s):
43 if s == "Tag Name VR VM":
45 elif s == "PS 3.6-2003":
47 elif s == "PS 3.6-2004":
49 patt = re.compile('^Page [0-9]+$')
54 def IsAStartingLine(self,s):
55 patt = re.compile('^\\([0-9a-fA-Fx]+,[0-9a-fA-F]+\\) (.*)$')
60 def IsAFullLine(self,s):
61 patt = re.compile('^\\([0-9a-fA-Fx]+,[0-9a-fA-F]+\\) (.*) [A-Z][A-Z] [0-9]$')
66 # FIXME this function could be avoided...
67 def IsSuspicious(self,s):
73 def AddOutputLine(self,s):
74 assert not self.IsAComment(s)
75 self._OutLines.append(s + '\n')
78 self._Infile = file(self._InputFilename, 'r')
79 for line in self._Infile.readlines():
80 line = line[:-1] # remove '\n'
81 if not self.IsAComment( line ):
82 if self.IsAStartingLine(line):
83 #print "Previous buffer:",self._PreviousBuffers
84 previousbuffer = ' '.join(self._PreviousBuffers)
85 if self.IsAStartingLine(previousbuffer):
86 if not self.IsSuspicious(previousbuffer):
87 self.AddOutputLine(previousbuffer)
89 # this case should not happen if I were to rewrite the
90 # thing I should be able to clean that
91 #print "Suspicious:", previousbuffer
92 #print "List is:", self._PreviousBuffers
93 s = self._PreviousBuffers[0]
94 if self.IsAFullLine(s):
95 # That means we have a weird line that does not start
96 # as usual (xxxx,xxxx) therefore we tried constructing
97 # a buffer using a the complete previous line...
98 #print "Full line:", s
100 s2 = ' '.join(self._PreviousBuffers[1:])
101 #print "Other Full line:", s2
102 self.AddOutputLine(s2)
104 # we have a suspicioulsy long line, so what that could
105 # happen, let's check:
106 if self.IsAFullLine(previousbuffer):
107 self.AddOutputLine(previousbuffer)
109 # This is the only case where we do not add
110 # previousbuffer to the _OutLines
111 print "Suspicious and Not a full line:", s
114 print "Not a buffer:", previousbuffer
115 # We can clean buffer, since only the case 'suspicious' +
116 # 'Not a full line' has not added buffer to the list
117 self._PreviousBuffers = []
118 # In all cases save the line for potentially growing this line
119 assert not self.IsAComment(line)
120 self._PreviousBuffers.append(line)
122 #print "Not a line",line
123 assert not self.IsAComment(line)
124 self._PreviousBuffers.append(line)
126 #print "Comment:",line
127 previousbuffer = ' '.join(self._PreviousBuffers)
128 if previousbuffer and self.IsAStartingLine(previousbuffer):
129 #print "This line is added:", previousbuffer
130 self.AddOutputLine( previousbuffer )
132 #print "Line is comment:", line
133 print "Buffer is:", previousbuffer
134 # Ok this is a comment we can safely clean the buffer:
135 self._PreviousBuffers = []
139 outfile = file(self._OutputFilename, 'w')
140 outfile.writelines( self._OutLines )
144 # Main function to call for parsing
151 class UIDParser(PdfTextParser):
152 def IsAStartingLine(self,s):
153 patt = re.compile('^1.2.840.10008.[0-9.]+ (.*)$')
159 def IsAFullLine(self,s):
160 patt = re.compile('^1.2.840.10008.[0-9.]+ (.*) PS ?[0-9].1?[0-9]$')
163 patt = re.compile('^1.2.840.10008.[0-9.]+ (.*) Well-known frame of reference$')
166 patt = re.compile('^1.2.840.10008.[0-9.]+ (.*) \\(Retired\\)$')
171 def IsAComment(self,s):
172 if PdfTextParser.IsAComment(self,s):
174 # else let's enhance the super class
175 patt = re.compile('^SPM2 (.*) http(.*)$')
180 def AddOutputLine(self,s):
181 if self.IsAFullLine(s):
182 return PdfTextParser.AddOutputLine(self,s)
183 print "Discarding:", s
189 class TransferSyntaxParser(UIDParser):
190 def IsAFullLine(self,s):
191 patt = re.compile('^(.*) Transfer Syntax PS ?[0-9].1?[0-9]$')
193 return UIDParser.IsAStartingLine(self,s)
199 pdftotext -f 19 -l 41 -raw -nopgbrk /tmp/Papyrus31Specif.pdf /tmp/Papyrus31Specif.txt
201 I need to do a second pass for pages:
202 #29 since I need to find [0-9.]+
203 #40,41 since it start with number in two columns !!
205 class PapyrusParser(PdfTextParser):
207 self._PreviousPage = 0
208 self._PreviousNumber = 0
209 PdfTextParser.__init__(self)
211 def IsAStartingLine(self,s):
212 patt = re.compile('^[A-Za-z \'\(\)]+ +\\([0-9A-F]+,[0-9A-F]+\\) +(.*)$')
217 def IsAFullLine(self,s):
218 patt = re.compile('^[A-Za-z \'\(\)]+ +\\([0-9A-F]+,[0-9A-F]+\\) +(.*)$')
223 def IsAComment(self,s):
225 if s == 'Attribute Name Tag Type Attribute Description':
228 # Indicate page #, spaces ending with only one number
229 # Sometime there is a line with only one number, we need to
230 # make sure that page # is strictly increasing
231 patt = re.compile('^[1-9][0-9]+$')
233 if( eval(s) > self._PreviousPage):
234 print "Page #", eval(s)
235 self._PreviousNumber = 0
236 self._PreviousPage = eval(s)
238 # Now within each page there is a comment that start with a #
239 # let's do the page approach wich reset at each page
240 patt = re.compile('^[0-9]+$')
242 print "Number #", eval(s)
243 self._PreviousNumber = eval(s)
247 def AddOutputLine(self,s):
248 assert not self.IsAComment(s)
249 s = s.replace('\n','')
250 #print "REMOVE return:", s
251 patt = re.compile('^([A-Za-z \'\(\)]+) (\\([0-9A-F]+,[0-9A-F]+\\)) ([0-9C]+) (.*)$')
253 ss = 'dummy (0000,0000) 0'
255 ss = m.group(2) + ' ' + m.group(3) + ' ' + m.group(1)
257 patt = re.compile('^([A-Za-z \'\(\)]+) (\\([0-9A-F]+,[0-9A-F]+\\)) (.*)$')
260 ss = m.group(2) + ' 0 ' + m.group(1)
261 self._OutLines.append(ss + '\n')
264 self._Infile = file(self._InputFilename, 'r')
265 for line in self._Infile.readlines():
266 line = line[:-1] # remove '\n'
267 if not self.IsAComment( line ):
268 if self.IsAStartingLine(line):
269 #print "Previous buffer:",self._PreviousBuffers
270 previousbuffer = ' '.join(self._PreviousBuffers)
271 if self.IsAFullLine(previousbuffer):
272 self.AddOutputLine(previousbuffer)
275 print "Not a buffer:", previousbuffer
276 # We can clean buffer, since only the case 'suspicious' +
277 # 'Not a full line' has not added buffer to the list
278 self._PreviousBuffers = []
279 # In all cases save the line for potentially growing this line
280 # just to be safe remove any white space at begining of string
281 assert not self.IsAComment(line)
282 self._PreviousBuffers.append(line.strip())
284 #print "Not a line",line
285 assert not self.IsAComment(line)
286 # just to be safe remove any white space at begining of string
287 self._PreviousBuffers.append(line.strip())
289 #print "Previous buffer:",self._PreviousBuffers
290 previousbuffer = ' '.join(self._PreviousBuffers)
291 if previousbuffer and self.IsAStartingLine(previousbuffer):
292 #print "This line is added:", previousbuffer
293 self.AddOutputLine( previousbuffer )
295 # #print "Line is comment:", line
296 # print "Buffer is:", previousbuffer
297 # Ok this is a comment we can safely clean the buffer:
298 self._PreviousBuffers = []
302 This class is meant to expand line like:
303 - (xxxx,xxxx to xxxx) xxxxxxxxxxxx
305 - (12xx, 3456) comment...
308 class DicomV3Expander:
310 self._InputFilename = ''
311 self._OutputFilename = ''
314 def SetInputFileName(self,s):
315 self._InputFilename = s
317 def SetOutputFileName(self,s):
318 self._OutputFilename = s
320 # Function to turn into lower case a tag:
321 # ex: (ABCD, EF01) -> (abcd, ef01)
322 def LowerCaseTag(self,s):
323 #print "Before:", s[:-1]
324 patt = re.compile('^(\\([0-9a-fA-F]+,[0-9a-fA-F]+\\))(.*)$')
329 return s1.lower() + s2
331 print "Impossible case:", s
334 def AddOutputLine(self,s):
335 if s.__class__ == list:
337 self._OutLines.append(i + '\n')
339 self._OutLines.append(s + '\n')
341 # Expand the line approriaetkly and also add it to the
343 def ExpandLine(self, s):
345 s = s[:-1] # remove \n
347 if self.NeedToExpansion(s, list):
348 self.AddOutputLine(list) # list != []
349 elif self.NeedXXExpansion(s, list):
350 self.AddOutputLine(list) # list != []
352 self.AddOutputLine(self.LowerCaseTag(s))
355 # (0020,3100 to 31FF) Source Image Ids RET
356 def NeedToExpansion(self,s, list):
357 patt = re.compile('^\\(([0-9a-fA-F]+),([0-9a-fA-F]+) to ([0-9a-fA-F]+)\\)(.*)$')
362 el_start = '0x'+m.group(2)
363 el_end = '0x'+m.group(3)
364 for i in range(eval(el_start), eval(el_end)):
366 l = '('+gr+','+el+')'+m.group(4)
372 # (50xx,1200) Number of Patient Related Studies IS 1
373 def NeedXXExpansion(self,s,list):
374 patt = re.compile('^\\(([0-9a-fA-F]+)xx,([0-9a-fA-F]+)\\)(.*)$')
378 gr_start = m.group(1)
380 #el_start = '0x'+m.group(2)
381 #el_end = '0x'+m.group(3)
382 start = '0x'+gr_start+'00'
383 end = '0x'+gr_start+'FF'
384 for i in range(eval(start), eval(end)):
386 l = '('+gr+','+el+')'+m.group(3)
393 outfile = file(self._OutputFilename, 'w')
394 outfile.writelines( self._OutLines )
398 infile = file(self._InputFilename,'r')
399 for line in infile.readlines():
400 # ExpandLine also LowerCase the line
401 self.ExpandLine(line) # l is [1,n] lines
406 if __name__ == "__main__":
407 argc = len(os.sys.argv )
409 print "Sorry, wrong list of args"
410 os.sys.exit(1) #error
412 inputfilename = os.sys.argv[1]
413 outputfilename = os.sys.argv[2]
414 tempfile = "/tmp/mytemp"
417 dp.SetInputFileName( inputfilename )
418 #dp.SetOutputFileName( outputfilename )
419 dp.SetOutputFileName( tempfile )
422 exp = DicomV3Expander()
423 exp.SetInputFileName( tempfile )
424 exp.SetOutputFileName( outputfilename )
427 dp = TransferSyntaxParser()
428 dp.SetInputFileName( inputfilename )
429 dp.SetOutputFileName( outputfilename )
433 dp.SetInputFileName( inputfilename )
434 dp.SetOutputFileName( outputfilename )
437 #print dp.IsAStartingLine( "(0004,1212) File-set Consistency Flag US 1\n" )