1 31 package org.pdfbox.util; 32 33 import java.io.IOException ; 34 35 import java.util.ArrayList ; 36 import java.util.HashMap ; 37 import java.util.Iterator ; 38 import java.util.List ; 39 import java.util.Map ; 40 import java.util.Properties ; 41 import java.util.Stack ; 42 43 import org.pdfbox.cos.COSObject; 44 import org.pdfbox.cos.COSStream; 45 import org.pdfbox.exceptions.WrappedIOException; 46 47 import org.pdfbox.pdmodel.PDPage; 48 import org.pdfbox.pdmodel.PDResources; 49 50 import org.pdfbox.pdmodel.font.PDFont; 51 52 import org.pdfbox.pdmodel.graphics.PDGraphicsState; 53 54 import org.pdfbox.util.operator.OperatorProcessor; 55 56 64 public class PDFStreamEngine 65 { 66 private static final byte[] SPACE_BYTES = { (byte)32 }; 67 68 private PDGraphicsState graphicsState = null; 69 70 private Matrix textMatrix = null; 71 private Matrix textLineMatrix = null; 72 private Stack graphicsStack = new Stack (); 73 75 private Map operators = new HashMap (); 76 77 private Stack streamResourcesStack = new Stack (); 78 79 private PDPage page; 80 81 private Map documentFontCache = new HashMap (); 82 83 87 private static class StreamResources 88 { 89 private Map fonts; 90 private Map colorSpaces; 91 private Map xobjects; 92 private Map graphicsStates; 93 private PDResources resources; 94 } 95 96 99 public PDFStreamEngine() 100 { 101 } 103 104 113 public PDFStreamEngine( Properties properties ) throws IOException 114 { 115 try 116 { 117 Iterator keys = properties.keySet().iterator(); 118 while( keys.hasNext() ) 119 { 120 String operator = (String )keys.next(); 121 String operatorClass = properties.getProperty( operator ); 122 OperatorProcessor op = (OperatorProcessor)Class.forName( operatorClass ).newInstance(); 123 registerOperatorProcessor(operator, op); 124 } 125 } 126 catch( Exception e ) 127 { 128 throw new WrappedIOException( e ); 129 } 130 } 131 132 138 public void registerOperatorProcessor( String operator, OperatorProcessor op ) 139 { 140 op.setContext( this ); 141 operators.put( operator, op ); 142 } 143 144 151 public void resetEngine() 152 { 153 documentFontCache.clear(); 154 } 155 156 166 public void processStream( PDPage aPage, PDResources resources, COSStream cosStream ) throws IOException 167 { 168 graphicsState = new PDGraphicsState(); 169 textMatrix = null; 170 textLineMatrix = null; 171 graphicsStack.clear(); 172 streamResourcesStack.clear(); 173 174 processSubStream( aPage, resources, cosStream ); 175 } 176 177 186 public void processSubStream( PDPage aPage, PDResources resources, COSStream cosStream ) throws IOException 187 { 188 page = aPage; 189 if( resources != null ) 190 { 191 StreamResources sr = new StreamResources(); 192 sr.fonts = resources.getFonts( documentFontCache ); 193 sr.colorSpaces = resources.getColorSpaces(); 194 sr.xobjects = resources.getXObjects(); 195 sr.graphicsStates = resources.getGraphicsStates(); 196 sr.resources = resources; 197 streamResourcesStack.push(sr); 198 } 199 try 200 { 201 List arguments = new ArrayList (); 202 List tokens = cosStream.getStreamTokens(); 203 if( tokens != null ) 204 { 205 Iterator iter = tokens.iterator(); 206 while( iter.hasNext() ) 207 { 208 Object next = iter.next(); 209 if( next instanceof COSObject ) 210 { 211 arguments.add( ((COSObject)next).getObject() ); 212 } 213 else if( next instanceof PDFOperator ) 214 { 215 processOperator( (PDFOperator)next, arguments ); 216 arguments = new ArrayList (); 217 } 218 else 219 { 220 arguments.add( next ); 221 } 222 } 223 } 224 } 225 finally 226 { 227 if( resources != null ) 228 { 229 streamResourcesStack.pop(); 230 } 231 } 232 233 } 234 235 241 protected void showCharacter( TextPosition text ) 242 { 243 } 245 246 254 public void showString( byte[] string ) throws IOException 255 { 256 float spaceWidth = 0; 257 float spacing = 0; 258 StringBuffer stringResult = new StringBuffer (string.length); 259 260 float characterHorizontalDisplacement = 0; 261 float characterVerticalDisplacement = 0; 262 float spaceDisplacement = 0; 263 float fontSize = graphicsState.getTextState().getFontSize(); 264 float horizontalScaling = graphicsState.getTextState().getHorizontalScalingPercent()/100f; 265 float verticalScaling = horizontalScaling; float rise = graphicsState.getTextState().getRise(); 267 final float wordSpacing = graphicsState.getTextState().getWordSpacing(); 268 final float characterSpacing = graphicsState.getTextState().getCharacterSpacing(); 269 float wordSpacingDisplacement = 0; 270 271 PDFont font = graphicsState.getTextState().getFont(); 272 273 float glyphSpaceToTextSpaceFactor = 1f/font.getFontMatrix().getValue( 0, 0 ); 276 float averageWidth = font.getAverageFontWidth(); 277 278 Matrix initialMatrix = new Matrix(); 279 initialMatrix.setValue(0,0,1); 280 initialMatrix.setValue(0,1,0); 281 initialMatrix.setValue(0,2,0); 282 initialMatrix.setValue(1,0,0); 283 initialMatrix.setValue(1,1,1); 284 initialMatrix.setValue(1,2,0); 285 initialMatrix.setValue(2,0,0); 286 initialMatrix.setValue(2,1,rise); 287 initialMatrix.setValue(2,2,1); 288 289 290 int codeLength = 1; 292 Matrix ctm = graphicsState.getCurrentTransformationMatrix(); 293 294 spaceDisplacement = (font.getFontWidth( SPACE_BYTES, 0, 1 )/glyphSpaceToTextSpaceFactor); 296 if( spaceDisplacement == 0 ) 297 { 298 spaceDisplacement = (averageWidth/glyphSpaceToTextSpaceFactor); 299 spaceDisplacement *= .80f; 302 } 303 int pageRotation = page.findRotation(); 304 Matrix trm = initialMatrix.multiply( textMatrix ).multiply( ctm ); 305 float x = trm.getValue(2,0); 306 float y = trm.getValue(2,1); 307 if( pageRotation == 0 ) 308 { 309 trm.setValue( 2,1, -y + page.findMediaBox().getHeight() ); 310 } 311 else if( pageRotation == 90 ) 312 { 313 trm.setValue( 2,0, y ); 314 trm.setValue( 2,1, x ); 315 } 316 else if( pageRotation == 270 ) 317 { 318 trm.setValue( 2,0, -y + page.findMediaBox().getHeight() ); 319 trm.setValue( 2,1, x ); 320 } 321 for( int i=0; i<string.length; i+=codeLength ) 322 { 323 codeLength = 1; 324 325 String c = font.encode( string, i, codeLength ); 326 if( c == null && i+1<string.length) 327 { 328 codeLength++; 330 c = font.encode( string, i, codeLength ); 331 } 332 stringResult.append( c ); 333 334 characterHorizontalDisplacement += (font.getFontWidth( string, i, codeLength )/glyphSpaceToTextSpaceFactor); 336 characterVerticalDisplacement = 337 Math.max( 338 characterVerticalDisplacement, 339 font.getFontHeight( string, i, codeLength)/glyphSpaceToTextSpaceFactor); 340 341 342 if( (string[i] == 0x20) && c.equals( " " ) ) 361 { 362 spacing += wordSpacing + characterSpacing; 363 } 364 else 365 { 366 spacing += characterSpacing; 367 } 368 371 } 372 373 float adjustment=0; 376 float ty = 0; 378 float tx = ((characterHorizontalDisplacement-adjustment/glyphSpaceToTextSpaceFactor)*fontSize + spacing) 379 *horizontalScaling; 380 381 float xScale = trm.getXScale(); 382 float yScale = trm.getYScale(); 383 float xPos = trm.getXPosition(); 384 float yPos = trm.getYPosition(); 385 spaceWidth = spaceDisplacement * xScale * fontSize; 386 wordSpacingDisplacement = wordSpacing*xScale * fontSize; 387 Matrix td = new Matrix(); 388 td.setValue( 2, 0, tx ); 389 td.setValue( 2, 1, ty ); 390 391 float xPosBefore = textMatrix.getXPosition(); 392 float yPosBefore = textMatrix.getYPosition(); 393 textMatrix = td.multiply( textMatrix ); 394 395 float totalStringWidth = 0; 396 float totalStringHeight = characterVerticalDisplacement * fontSize * yScale; 397 if( pageRotation == 0 ) 398 { 399 totalStringWidth = (textMatrix.getXPosition() - xPosBefore); 400 } 401 else if( pageRotation == 90 ) 402 { 403 totalStringWidth = (textMatrix.getYPosition() - yPosBefore); 404 } 405 else if( pageRotation == 270 ) 406 { 407 totalStringWidth = (yPosBefore - textMatrix.getYPosition()); 408 } 409 showCharacter( 410 new TextPosition( 411 xPos, 412 yPos, 413 xScale, 414 yScale, 415 totalStringWidth, 416 totalStringHeight, 417 spaceWidth, 418 stringResult.toString(), 419 font, 420 fontSize, 421 wordSpacingDisplacement )); 422 } 423 424 432 public void processOperator( String operation, List arguments ) throws IOException 433 { 434 PDFOperator oper = PDFOperator.getOperator( operation ); 435 processOperator( oper, arguments ); 436 } 437 438 446 protected void processOperator( PDFOperator operator, List arguments ) throws IOException 447 { 448 String operation = operator.getOperation(); 449 OperatorProcessor processor = (OperatorProcessor)operators.get( operation ); 450 if( processor != null ) 451 { 452 processor.process( operator, arguments ); 453 } 454 } 455 456 459 public Map getColorSpaces() 460 { 461 return ((StreamResources) streamResourcesStack.peek()).colorSpaces; 462 } 463 464 467 public Map getXObjects() 468 { 469 return ((StreamResources) streamResourcesStack.peek()).xobjects; 470 } 471 472 475 public void setColorSpaces(Map value) 476 { 477 ((StreamResources) streamResourcesStack.peek()).colorSpaces = value; 478 } 479 482 public Map getFonts() 483 { 484 return ((StreamResources) streamResourcesStack.peek()).fonts; 485 } 486 489 public void setFonts(Map value) 490 { 491 ((StreamResources) streamResourcesStack.peek()).fonts = value; 492 } 493 496 public Stack getGraphicsStack() 497 { 498 return graphicsStack; 499 } 500 503 public void setGraphicsStack(Stack value) 504 { 505 graphicsStack = value; 506 } 507 510 public PDGraphicsState getGraphicsState() 511 { 512 return graphicsState; 513 } 514 517 public void setGraphicsState(PDGraphicsState value) 518 { 519 graphicsState = value; 520 } 521 524 public Map getGraphicsStates() 525 { 526 return ((StreamResources) streamResourcesStack.peek()).graphicsStates; 527 } 528 531 public void setGraphicsStates(Map value) 532 { 533 ((StreamResources) streamResourcesStack.peek()).graphicsStates = value; 534 } 535 538 public Matrix getTextLineMatrix() 539 { 540 return textLineMatrix; 541 } 542 545 public void setTextLineMatrix(Matrix value) 546 { 547 textLineMatrix = value; 548 } 549 552 public Matrix getTextMatrix() 553 { 554 return textMatrix; 555 } 556 559 public void setTextMatrix(Matrix value) 560 { 561 textMatrix = value; 562 } 563 566 public PDResources getResources() 567 { 568 return ((StreamResources) streamResourcesStack.peek()).resources; 569 } 570 571 576 public PDPage getCurrentPage() 577 { 578 return page; 579 } 580 } | Popular Tags |