1 17 18 19 20 package org.apache.fop.fo; 21 22 import java.util.List ; 23 import java.util.Stack ; 24 import org.apache.fop.fo.flow.Block; 25 import org.apache.fop.fo.flow.Character; 26 import org.apache.fop.util.CharUtilities; 27 28 66 public class XMLWhiteSpaceHandler { 67 68 69 private boolean inWhiteSpace = false; 70 71 private boolean afterLinefeed = true; 72 73 private int nonWhiteSpaceCount; 74 75 private Block currentBlock; 76 private FObj currentFO; 77 private int linefeedTreatment; 78 private int whiteSpaceTreatment; 79 private int whiteSpaceCollapse; 80 private FONode nextChild; 81 private boolean endOfBlock; 82 private boolean nextChildIsBlockLevel; 83 private RecursiveCharIterator charIter; 84 85 private List discardableFOCharacters; 86 private List pendingInlines; 87 private Stack nestedBlockStack = new java.util.Stack (); 88 private CharIterator firstWhiteSpaceInSeq; 89 90 96 public void addDiscardableFOChar(Character foChar) { 97 if (discardableFOCharacters == null) { 98 discardableFOCharacters = new java.util.ArrayList (); 99 } 100 discardableFOCharacters.add(foChar); 101 } 102 103 109 public void handleWhiteSpace(FObjMixed fo, FONode firstTextNode) { 110 111 int foId = fo.getNameId(); 112 113 if (foId == Constants.FO_BLOCK) { 114 if (nextChild != null && currentBlock != null) { 115 118 nestedBlockStack.push(currentBlock); 119 } 120 currentBlock = (Block) fo; 121 } else if (foId == Constants.FO_RETRIEVE_MARKER) { 122 123 FONode ancestor = fo; 124 do { 125 ancestor = ancestor.getParent(); 126 } while (ancestor.getNameId() != Constants.FO_BLOCK 127 && ancestor.getNameId() != Constants.FO_STATIC_CONTENT); 128 129 if (ancestor.getNameId() == Constants.FO_BLOCK) { 130 currentBlock = (Block) ancestor; 131 } 132 } 133 134 if (currentBlock != null) { 135 linefeedTreatment = currentBlock.getLinefeedTreatment(); 136 whiteSpaceCollapse = currentBlock.getWhitespaceCollapse(); 137 whiteSpaceTreatment = currentBlock.getWhitespaceTreatment(); 138 } else { 139 linefeedTreatment = Constants.EN_TREAT_AS_SPACE; 140 whiteSpaceCollapse = Constants.EN_TRUE; 141 whiteSpaceTreatment = Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED; 142 } 143 144 currentFO = fo; 145 146 if (firstTextNode == null) { 147 return; 149 } 150 151 charIter = new RecursiveCharIterator(fo, firstTextNode); 152 inWhiteSpace = false; 153 154 if (currentFO == currentBlock 155 || currentBlock == null 156 || (foId == Constants.FO_RETRIEVE_MARKER 157 && currentFO.getParent() == currentBlock)) { 158 int textNodeIndex = fo.childNodes.indexOf(firstTextNode); 159 afterLinefeed = ( 160 (textNodeIndex == 0) 161 || (textNodeIndex > 0 162 && ((FONode) fo.childNodes.get(textNodeIndex - 1)) 163 .getNameId() == Constants.FO_BLOCK)); 164 } 165 166 endOfBlock = (nextChild == null && currentFO == currentBlock); 167 168 if (nextChild != null) { 169 int nextChildId = this.nextChild.getNameId(); 170 nextChildIsBlockLevel = ( 171 nextChildId == Constants.FO_BLOCK 172 || nextChildId == Constants.FO_TABLE_AND_CAPTION 173 || nextChildId == Constants.FO_TABLE 174 || nextChildId == Constants.FO_LIST_BLOCK 175 || nextChildId == Constants.FO_BLOCK_CONTAINER); 176 } else { 177 nextChildIsBlockLevel = false; 178 } 179 180 handleWhiteSpace(); 181 182 if (currentFO == currentBlock 183 && pendingInlines != null 184 && !pendingInlines.isEmpty()) { 185 186 if (endOfBlock || nextChildIsBlockLevel) { 187 if (nonWhiteSpaceCount == 0) { 188 189 PendingInline p; 190 for (int i = pendingInlines.size(); --i >= 0;) { 191 p = (PendingInline)pendingInlines.get(i); 192 charIter = (RecursiveCharIterator)p.firstTrailingWhiteSpace; 193 handleWhiteSpace(); 194 pendingInlines.remove(p); 195 } 196 } else { 197 200 pendingInlines.clear(); 201 } 202 } 203 } 204 205 if (nextChild == null) { 206 if (currentFO != currentBlock) { 207 208 if (nonWhiteSpaceCount > 0 && pendingInlines != null) { 209 212 pendingInlines.clear(); 213 } 214 if (inWhiteSpace) { 215 217 addPendingInline(fo); 218 } 219 } else { 220 222 if (!nestedBlockStack.empty()) { 223 currentBlock = (Block) nestedBlockStack.pop(); 224 } else { 225 currentBlock = null; 226 } 227 currentFO = null; 228 charIter = null; 229 } 230 } 231 } 232 233 241 public void handleWhiteSpace(FObjMixed fo, FONode firstTextNode, FONode nextChild) { 242 this.nextChild = nextChild; 243 handleWhiteSpace(fo, firstTextNode); 244 this.nextChild = null; 245 } 246 247 private void handleWhiteSpace() { 248 249 EOLchecker lfCheck = new EOLchecker(charIter); 250 251 nonWhiteSpaceCount = 0; 252 253 while (charIter.hasNext()) { 254 if (!inWhiteSpace) { 255 firstWhiteSpaceInSeq = charIter.mark(); 256 } 257 char currentChar = charIter.nextChar(); 258 int currentCharClass = CharUtilities.classOf(currentChar); 259 if (currentCharClass == CharUtilities.LINEFEED 260 && linefeedTreatment == Constants.EN_TREAT_AS_SPACE) { 261 currentChar = '\u0020'; 264 charIter.replaceChar('\u0020'); 265 currentCharClass = CharUtilities.classOf(currentChar); 266 } 267 switch (CharUtilities.classOf(currentChar)) { 268 case CharUtilities.XMLWHITESPACE: 269 if (inWhiteSpace 271 && whiteSpaceCollapse == Constants.EN_TRUE) { 272 charIter.remove(); 275 } else { 276 boolean bIgnore = false; 278 279 switch (whiteSpaceTreatment) { 280 case Constants.EN_IGNORE: 281 bIgnore = true; 282 break; 283 case Constants.EN_IGNORE_IF_BEFORE_LINEFEED: 284 bIgnore = lfCheck.beforeLinefeed(); 285 break; 286 case Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED: 287 bIgnore = afterLinefeed 288 || lfCheck.beforeLinefeed(); 289 break; 290 case Constants.EN_IGNORE_IF_AFTER_LINEFEED: 291 bIgnore = afterLinefeed; 292 break; 293 case Constants.EN_PRESERVE: 294 break; 296 default: 297 } 299 if (bIgnore) { 301 charIter.remove(); 302 } else { 303 inWhiteSpace = true; 305 if (currentChar != '\u0020') { 306 charIter.replaceChar('\u0020'); 307 } 308 } 309 } 310 break; 311 312 case CharUtilities.LINEFEED: 313 switch (linefeedTreatment) { 315 case Constants.EN_IGNORE: 316 charIter.remove(); 317 break; 318 case Constants.EN_TREAT_AS_ZERO_WIDTH_SPACE: 319 charIter.replaceChar(CharUtilities.ZERO_WIDTH_SPACE); 320 inWhiteSpace = false; 321 break; 322 case Constants.EN_PRESERVE: 323 lfCheck.reset(); 324 inWhiteSpace = false; 325 afterLinefeed = true; break; 327 default: 328 } 330 break; 331 332 case CharUtilities.EOT: 333 338 default: 339 inWhiteSpace = false; 341 afterLinefeed = false; 342 nonWhiteSpaceCount++; 343 lfCheck.reset(); 344 break; 345 } 346 } 347 if (discardableFOCharacters != null 348 && !discardableFOCharacters.isEmpty()) { 349 currentFO.childNodes.removeAll(discardableFOCharacters); 350 discardableFOCharacters.clear(); 351 } 352 } 353 354 private void addPendingInline(FObjMixed fo) { 355 if (pendingInlines == null) { 356 pendingInlines = new java.util.ArrayList (5); 357 } 358 pendingInlines.add(new PendingInline(fo, firstWhiteSpaceInSeq)); 359 } 360 361 367 private class EOLchecker { 368 private boolean nextIsEOL = false; 369 private RecursiveCharIterator charIter; 370 371 EOLchecker(CharIterator charIter) { 372 this.charIter = (RecursiveCharIterator) charIter; 373 } 374 375 boolean beforeLinefeed() { 376 if (!nextIsEOL) { 377 CharIterator lfIter = charIter.mark(); 378 while (lfIter.hasNext()) { 379 int charClass = CharUtilities.classOf(lfIter.nextChar()); 380 if (charClass == CharUtilities.LINEFEED) { 381 if (linefeedTreatment == Constants.EN_PRESERVE) { 382 nextIsEOL = true; 383 return nextIsEOL; 384 } 385 } else if (charClass != CharUtilities.XMLWHITESPACE) { 386 return nextIsEOL; 387 } 388 } 389 nextIsEOL = nextChildIsBlockLevel || endOfBlock; 393 } 394 return nextIsEOL; 395 } 396 397 void reset() { 398 nextIsEOL = false; 399 } 400 } 401 402 407 private class PendingInline { 408 protected FObjMixed fo; 409 protected CharIterator firstTrailingWhiteSpace; 410 411 PendingInline(FObjMixed fo, CharIterator firstTrailingWhiteSpace) { 412 this.fo = fo; 413 this.firstTrailingWhiteSpace = firstTrailingWhiteSpace; 414 } 415 } 416 } 417 | Popular Tags |