You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AreaTreeParser.java 51KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.area;
  19. import java.awt.Color;
  20. import java.awt.Rectangle;
  21. import java.awt.geom.Rectangle2D;
  22. import java.io.FileNotFoundException;
  23. import java.io.IOException;
  24. import java.nio.CharBuffer;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import java.util.Stack;
  29. import javax.xml.transform.Source;
  30. import javax.xml.transform.Transformer;
  31. import javax.xml.transform.TransformerConfigurationException;
  32. import javax.xml.transform.TransformerException;
  33. import javax.xml.transform.dom.DOMResult;
  34. import javax.xml.transform.sax.SAXResult;
  35. import javax.xml.transform.sax.SAXTransformerFactory;
  36. import javax.xml.transform.sax.TransformerHandler;
  37. import org.apache.commons.logging.Log;
  38. import org.apache.commons.logging.LogFactory;
  39. import org.w3c.dom.DOMImplementation;
  40. import org.w3c.dom.Document;
  41. import org.xml.sax.Attributes;
  42. import org.xml.sax.ContentHandler;
  43. import org.xml.sax.Locator;
  44. import org.xml.sax.SAXException;
  45. import org.xml.sax.helpers.AttributesImpl;
  46. import org.xml.sax.helpers.DefaultHandler;
  47. import org.apache.xmlgraphics.image.loader.ImageException;
  48. import org.apache.xmlgraphics.image.loader.ImageInfo;
  49. import org.apache.xmlgraphics.image.loader.ImageManager;
  50. import org.apache.xmlgraphics.image.loader.ImageSessionContext;
  51. import org.apache.xmlgraphics.util.QName;
  52. import org.apache.fop.apps.FOUserAgent;
  53. import org.apache.fop.area.Trait.Background;
  54. import org.apache.fop.area.Trait.InternalLink;
  55. import org.apache.fop.area.inline.AbstractTextArea;
  56. import org.apache.fop.area.inline.ForeignObject;
  57. import org.apache.fop.area.inline.Image;
  58. import org.apache.fop.area.inline.InlineArea;
  59. import org.apache.fop.area.inline.InlineBlockParent;
  60. import org.apache.fop.area.inline.InlineParent;
  61. import org.apache.fop.area.inline.Leader;
  62. import org.apache.fop.area.inline.Space;
  63. import org.apache.fop.area.inline.SpaceArea;
  64. import org.apache.fop.area.inline.TextArea;
  65. import org.apache.fop.area.inline.Viewport;
  66. import org.apache.fop.area.inline.WordArea;
  67. import org.apache.fop.events.ResourceEventProducer;
  68. import org.apache.fop.fo.Constants;
  69. import org.apache.fop.fo.ElementMappingRegistry;
  70. import org.apache.fop.fo.expr.PropertyException;
  71. import org.apache.fop.fo.extensions.ExtensionAttachment;
  72. import org.apache.fop.fonts.Font;
  73. import org.apache.fop.fonts.FontInfo;
  74. import org.apache.fop.traits.BorderProps;
  75. import org.apache.fop.util.ColorUtil;
  76. import org.apache.fop.util.ContentHandlerFactory;
  77. import org.apache.fop.util.ContentHandlerFactoryRegistry;
  78. import org.apache.fop.util.ConversionUtils;
  79. import org.apache.fop.util.DefaultErrorListener;
  80. import org.apache.fop.util.XMLUtil;
  81. /**
  82. * This is a parser for the area tree XML (intermediate format) which is used to reread an area
  83. * tree (or part of it) into memory again for rendering to the final output format.
  84. */
  85. public class AreaTreeParser {
  86. /** Logger instance */
  87. protected static Log log = LogFactory.getLog(AreaTreeParser.class);
  88. private static SAXTransformerFactory tFactory
  89. = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
  90. /**
  91. * Parses an intermediate file (area tree XML) into an AreaTreeModel instance by adding
  92. * pages to it.
  93. * @param src the Source instance pointing to the intermediate file
  94. * @param treeModel the AreaTreeModel that the parsed pages are added to
  95. * @param userAgent the user agent
  96. * @throws TransformerException if an error occurs while parsing the area tree XML
  97. */
  98. public void parse(Source src, AreaTreeModel treeModel, FOUserAgent userAgent)
  99. throws TransformerException {
  100. Transformer transformer = tFactory.newTransformer();
  101. transformer.setErrorListener(new DefaultErrorListener(log));
  102. SAXResult res = new SAXResult(getContentHandler(treeModel, userAgent));
  103. transformer.transform(src, res);
  104. }
  105. /**
  106. * Creates a new ContentHandler instance that you can send the area tree XML to. The parsed
  107. * pages are added to the AreaTreeModel instance you pass in as a parameter.
  108. * @param treeModel the AreaTreeModel that the parsed pages are added to
  109. * @param userAgent the user agent
  110. * @return the ContentHandler instance to receive the SAX stream from the area tree XML
  111. */
  112. public ContentHandler getContentHandler(AreaTreeModel treeModel, FOUserAgent userAgent) {
  113. ElementMappingRegistry elementMappingRegistry
  114. = userAgent.getFactory().getElementMappingRegistry();
  115. return new Handler(treeModel, userAgent, elementMappingRegistry);
  116. }
  117. private static class Handler extends DefaultHandler {
  118. private Map makers = new java.util.HashMap();
  119. private AreaTreeModel treeModel;
  120. private FOUserAgent userAgent;
  121. private ElementMappingRegistry elementMappingRegistry;
  122. private Attributes lastAttributes;
  123. private CharBuffer content = CharBuffer.allocate(64);
  124. private boolean ignoreCharacters = true;
  125. private PageViewport currentPageViewport;
  126. private Map pageViewportsByKey = new java.util.HashMap();
  127. // set of "ID firsts" that have already been assigned to a PV:
  128. private Set idFirstsAssigned = new java.util.HashSet();
  129. private Stack areaStack = new Stack();
  130. private boolean firstFlow;
  131. private Stack delegateStack = new Stack();
  132. private ContentHandler delegate;
  133. private DOMImplementation domImplementation;
  134. private Locator locator;
  135. public Handler(AreaTreeModel treeModel, FOUserAgent userAgent,
  136. ElementMappingRegistry elementMappingRegistry) {
  137. this.treeModel = treeModel;
  138. this.userAgent = userAgent;
  139. this.elementMappingRegistry = elementMappingRegistry;
  140. makers.put("areaTree", new AreaTreeMaker());
  141. makers.put("page", new PageMaker());
  142. makers.put("pageSequence", new PageSequenceMaker());
  143. makers.put("title", new TitleMaker());
  144. makers.put("pageViewport", new PageViewportMaker());
  145. makers.put("regionViewport", new RegionViewportMaker());
  146. makers.put("regionBefore", new RegionBeforeMaker());
  147. makers.put("regionAfter", new RegionAfterMaker());
  148. makers.put("regionStart", new RegionStartMaker());
  149. makers.put("regionEnd", new RegionEndMaker());
  150. makers.put("regionBody", new RegionBodyMaker());
  151. makers.put("flow", new FlowMaker());
  152. makers.put("mainReference", new MainReferenceMaker());
  153. makers.put("span", new SpanMaker());
  154. makers.put("footnote", new FootnoteMaker());
  155. makers.put("beforeFloat", new BeforeFloatMaker());
  156. makers.put("block", new BlockMaker());
  157. makers.put("lineArea", new LineAreaMaker());
  158. makers.put("inline", new InlineMaker());
  159. makers.put("inlineparent", new InlineParentMaker());
  160. makers.put("inlineblockparent", new InlineBlockParentMaker());
  161. makers.put("text", new TextMaker());
  162. makers.put("word", new WordMaker());
  163. makers.put("space", new SpaceMaker());
  164. makers.put("leader", new LeaderMaker());
  165. makers.put("viewport", new ViewportMaker());
  166. makers.put("image", new ImageMaker());
  167. makers.put("foreignObject", new ForeignObjectMaker());
  168. makers.put("bookmarkTree", new BookmarkTreeMaker());
  169. makers.put("bookmark", new BookmarkMaker());
  170. makers.put("destination", new DestinationMaker());
  171. }
  172. private Area findAreaType(Class clazz) {
  173. if (areaStack.size() > 0) {
  174. int pos = areaStack.size() - 1;
  175. Object obj = null;
  176. while (pos >= 0 && !(clazz.isInstance(obj = areaStack.get(pos)))) {
  177. pos--;
  178. }
  179. if (pos >= 0) {
  180. return (Area)obj;
  181. }
  182. }
  183. return null;
  184. }
  185. private RegionViewport getCurrentRegionViewport() {
  186. return (RegionViewport)findAreaType(RegionViewport.class);
  187. }
  188. private BodyRegion getCurrentBodyRegion() {
  189. return (BodyRegion)findAreaType(BodyRegion.class);
  190. }
  191. private BlockParent getCurrentBlockParent() {
  192. return (BlockParent)findAreaType(BlockParent.class);
  193. }
  194. private AbstractTextArea getCurrentText() {
  195. return (AbstractTextArea)findAreaType(AbstractTextArea.class);
  196. }
  197. private Viewport getCurrentViewport() {
  198. return (Viewport)findAreaType(Viewport.class);
  199. }
  200. /** {@inheritDoc} */
  201. public void setDocumentLocator(Locator locator) {
  202. this.locator = locator;
  203. }
  204. private Locator getLocator() {
  205. return this.locator;
  206. }
  207. /** {@inheritDoc} */
  208. public void startElement(String uri, String localName, String qName, Attributes attributes)
  209. throws SAXException {
  210. if (delegate != null) {
  211. delegateStack.push(qName);
  212. delegate.startElement(uri, localName, qName, attributes);
  213. } else if (domImplementation != null) {
  214. //domImplementation is set so we need to start a new DOM building sub-process
  215. TransformerHandler handler;
  216. try {
  217. handler = tFactory.newTransformerHandler();
  218. } catch (TransformerConfigurationException e) {
  219. throw new SAXException("Error creating a new TransformerHandler", e);
  220. }
  221. Document doc = domImplementation.createDocument(uri, qName, null);
  222. //It's easier to work with an empty document, so remove the root element
  223. doc.removeChild(doc.getDocumentElement());
  224. handler.setResult(new DOMResult(doc));
  225. Area parent = (Area)areaStack.peek();
  226. ((ForeignObject)parent).setDocument(doc);
  227. //activate delegate for nested foreign document
  228. domImplementation = null; //Not needed anymore now
  229. this.delegate = handler;
  230. delegateStack.push(qName);
  231. delegate.startDocument();
  232. delegate.startElement(uri, localName, qName, attributes);
  233. } else {
  234. lastAttributes = new AttributesImpl(attributes);
  235. boolean handled = true;
  236. if ("".equals(uri)) {
  237. Maker maker = (Maker)makers.get(localName);
  238. content.clear();
  239. ignoreCharacters = true;
  240. if (maker != null) {
  241. ignoreCharacters = maker.ignoreCharacters();
  242. maker.startElement(attributes);
  243. } else if ("extension-attachments".equals(localName)) {
  244. //TODO implement me
  245. } else {
  246. handled = false;
  247. }
  248. } else {
  249. ContentHandlerFactoryRegistry registry
  250. = userAgent.getFactory().getContentHandlerFactoryRegistry();
  251. ContentHandlerFactory factory = registry.getFactory(uri);
  252. if (factory != null) {
  253. delegate = factory.createContentHandler();
  254. delegateStack.push(qName);
  255. delegate.startDocument();
  256. delegate.startElement(uri, localName, qName, attributes);
  257. } else {
  258. handled = false;
  259. }
  260. }
  261. if (!handled) {
  262. if (uri == null || uri.length() == 0) {
  263. throw new SAXException("Unhandled element " + localName
  264. + " in namespace: " + uri);
  265. } else {
  266. log.warn("Unhandled element " + localName
  267. + " in namespace: " + uri);
  268. }
  269. }
  270. }
  271. }
  272. /** {@inheritDoc} */
  273. public void endElement(String uri, String localName, String qName) throws SAXException {
  274. if (delegate != null) {
  275. delegate.endElement(uri, localName, qName);
  276. delegateStack.pop();
  277. if (delegateStack.size() == 0) {
  278. delegate.endDocument();
  279. if (delegate instanceof ContentHandlerFactory.ObjectSource) {
  280. Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject();
  281. handleExternallyGeneratedObject(obj);
  282. }
  283. delegate = null; //Sub-document is processed, return to normal processing
  284. }
  285. } else {
  286. if ("".equals(uri)) {
  287. Maker maker = (Maker)makers.get(localName);
  288. if (maker != null) {
  289. maker.endElement();
  290. content.clear();
  291. }
  292. ignoreCharacters = true;
  293. } else {
  294. //log.debug("Ignoring " + localName + " in namespace: " + uri);
  295. }
  296. }
  297. }
  298. // ============== Maker classes for the area tree objects =============
  299. private static interface Maker {
  300. void startElement(Attributes attributes) throws SAXException;
  301. void endElement();
  302. boolean ignoreCharacters();
  303. }
  304. private abstract class AbstractMaker implements Maker {
  305. public void startElement(Attributes attributes) throws SAXException {
  306. //nop
  307. }
  308. public void endElement() {
  309. //nop
  310. }
  311. public boolean ignoreCharacters() {
  312. return true;
  313. }
  314. }
  315. private class AreaTreeMaker extends AbstractMaker {
  316. public void startElement(Attributes attributes) {
  317. // In case the Handler is reused:
  318. idFirstsAssigned.clear();
  319. }
  320. }
  321. private class PageSequenceMaker extends AbstractMaker {
  322. public void startElement(Attributes attributes) {
  323. PageSequence pageSequence = new PageSequence(null);
  324. String lang = attributes.getValue("language");
  325. pageSequence.setLanguage(lang);
  326. String country = attributes.getValue("country");
  327. pageSequence.setCountry(country);
  328. transferForeignObjects(attributes, pageSequence);
  329. areaStack.push(pageSequence);
  330. }
  331. }
  332. private class TitleMaker extends AbstractMaker {
  333. public void startElement(Attributes attributes) {
  334. LineArea line = new LineArea();
  335. transferForeignObjects(attributes, line);
  336. areaStack.push(line);
  337. }
  338. public void endElement() {
  339. LineArea line = (LineArea)areaStack.pop();
  340. PageSequence pageSequence = (PageSequence)areaStack.peek();
  341. pageSequence.setTitle(line);
  342. }
  343. }
  344. private class PageViewportMaker extends AbstractMaker {
  345. public void startElement(Attributes attributes) {
  346. if (!areaStack.isEmpty()) {
  347. PageSequence pageSequence = (PageSequence)areaStack.peek();
  348. treeModel.startPageSequence(pageSequence);
  349. areaStack.pop();
  350. }
  351. if (currentPageViewport != null) {
  352. throw new IllegalStateException("currentPageViewport must be null");
  353. }
  354. Rectangle viewArea = XMLUtil.getAttributeAsRectangle(attributes, "bounds");
  355. int pageNumber = XMLUtil.getAttributeAsInt(attributes, "nr", -1);
  356. String key = attributes.getValue("key");
  357. String pageNumberString = attributes.getValue("formatted-nr");
  358. String pageMaster = attributes.getValue("simple-page-master-name");
  359. boolean blank = XMLUtil.getAttributeAsBoolean(attributes, "blank", false);
  360. currentPageViewport = new PageViewport(viewArea,
  361. pageNumber, pageNumberString,
  362. pageMaster, blank);
  363. transferForeignObjects(attributes, currentPageViewport);
  364. currentPageViewport.setKey(key);
  365. pageViewportsByKey.put(key, currentPageViewport);
  366. }
  367. }
  368. private class PageMaker extends AbstractMaker {
  369. public void startElement(Attributes attributes) {
  370. Page p = new Page();
  371. currentPageViewport.setPage(p);
  372. }
  373. public void endElement() {
  374. treeModel.addPage(currentPageViewport);
  375. currentPageViewport = null;
  376. }
  377. }
  378. private class RegionViewportMaker extends AbstractMaker {
  379. public void startElement(Attributes attributes) {
  380. RegionViewport rv = getCurrentRegionViewport();
  381. if (rv != null) {
  382. throw new IllegalStateException("Current RegionViewport must be null");
  383. }
  384. Rectangle2D viewArea = XMLUtil.getAttributeAsRectangle2D(attributes, "rect");
  385. rv = new RegionViewport(viewArea);
  386. transferForeignObjects(attributes, rv);
  387. rv.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clipped", false));
  388. setAreaAttributes(attributes, rv);
  389. setTraits(attributes, rv, SUBSET_COMMON);
  390. setTraits(attributes, rv, SUBSET_BOX);
  391. setTraits(attributes, rv, SUBSET_COLOR);
  392. areaStack.push(rv);
  393. }
  394. public void endElement() {
  395. assertObjectOfClass(areaStack.pop(), RegionViewport.class);
  396. }
  397. }
  398. private class RegionBeforeMaker extends AbstractMaker {
  399. public void startElement(Attributes attributes) {
  400. pushNewRegionReference(attributes, Constants.FO_REGION_BEFORE);
  401. }
  402. public void endElement() {
  403. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  404. }
  405. }
  406. private class RegionAfterMaker extends AbstractMaker {
  407. public void startElement(Attributes attributes) {
  408. pushNewRegionReference(attributes, Constants.FO_REGION_AFTER);
  409. }
  410. public void endElement() {
  411. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  412. }
  413. }
  414. private class RegionStartMaker extends AbstractMaker {
  415. public void startElement(Attributes attributes) {
  416. pushNewRegionReference(attributes, Constants.FO_REGION_START);
  417. }
  418. public void endElement() {
  419. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  420. }
  421. }
  422. private class RegionEndMaker extends AbstractMaker {
  423. public void startElement(Attributes attributes) {
  424. pushNewRegionReference(attributes, Constants.FO_REGION_END);
  425. }
  426. public void endElement() {
  427. assertObjectOfClass(areaStack.pop(), RegionReference.class);
  428. }
  429. }
  430. private class RegionBodyMaker extends AbstractMaker {
  431. public void startElement(Attributes attributes) {
  432. BodyRegion body = getCurrentBodyRegion();
  433. if (body != null) {
  434. throw new IllegalStateException("Current BodyRegion must be null");
  435. }
  436. String regionName = attributes.getValue("name");
  437. int columnCount = XMLUtil.getAttributeAsInt(attributes, "columnCount", 1);
  438. int columnGap = XMLUtil.getAttributeAsInt(attributes, "columnGap", 0);
  439. RegionViewport rv = getCurrentRegionViewport();
  440. body = new BodyRegion(Constants.FO_REGION_BODY,
  441. regionName, rv, columnCount, columnGap);
  442. transferForeignObjects(attributes, body);
  443. body.setCTM(getAttributeAsCTM(attributes, "ctm"));
  444. setAreaAttributes(attributes, body);
  445. setTraits(attributes, body, SUBSET_BORDER_PADDING);
  446. rv.setRegionReference(body);
  447. currentPageViewport.getPage().setRegionViewport(
  448. Constants.FO_REGION_BODY, rv);
  449. areaStack.push(body);
  450. }
  451. public void endElement() {
  452. assertObjectOfClass(areaStack.pop(), BodyRegion.class);
  453. }
  454. }
  455. private class FlowMaker extends AbstractMaker {
  456. public void startElement(Attributes attributes) {
  457. BodyRegion body = getCurrentBodyRegion();
  458. if (!firstFlow) {
  459. body.getMainReference().getCurrentSpan().moveToNextFlow();
  460. } else {
  461. firstFlow = false;
  462. }
  463. NormalFlow flow = body.getMainReference().getCurrentSpan().getCurrentFlow();
  464. transferForeignObjects(attributes, flow);
  465. setAreaAttributes(attributes, flow);
  466. areaStack.push(flow);
  467. }
  468. public void endElement() {
  469. assertObjectOfClass(areaStack.pop(), NormalFlow.class);
  470. }
  471. }
  472. private class MainReferenceMaker extends AbstractMaker {
  473. public void startElement(Attributes attributes) {
  474. //mainReference is created by the BodyRegion
  475. MainReference mr = getCurrentBodyRegion().getMainReference();
  476. transferForeignObjects(attributes, mr);
  477. setAreaAttributes(attributes, mr);
  478. }
  479. }
  480. private class SpanMaker extends AbstractMaker {
  481. public void startElement(Attributes attributes) {
  482. int ipd = XMLUtil.getAttributeAsInt(attributes, "ipd", 0);
  483. int columnCount = XMLUtil.getAttributeAsInt(attributes, "columnCount", 1);
  484. BodyRegion body = getCurrentBodyRegion();
  485. Span span = new Span(columnCount,
  486. body.getColumnGap(), ipd);
  487. transferForeignObjects(attributes, span);
  488. setAreaAttributes(attributes, span);
  489. body.getMainReference().getSpans().add(span);
  490. firstFlow = true;
  491. }
  492. }
  493. private class FootnoteMaker extends AbstractMaker {
  494. public void startElement(Attributes attributes) {
  495. Footnote fn = getCurrentBodyRegion().getFootnote();
  496. transferForeignObjects(attributes, fn);
  497. fn.setTop(XMLUtil.getAttributeAsInt(attributes, "top-offset", 0));
  498. areaStack.push(fn);
  499. }
  500. public void endElement() {
  501. assertObjectOfClass(areaStack.pop(), Footnote.class);
  502. }
  503. }
  504. private class BeforeFloatMaker extends AbstractMaker {
  505. public void startElement(Attributes attributes) {
  506. BeforeFloat bf = getCurrentBodyRegion().getBeforeFloat();
  507. transferForeignObjects(attributes, bf);
  508. areaStack.push(bf);
  509. }
  510. public void endElement() {
  511. assertObjectOfClass(areaStack.pop(), BeforeFloat.class);
  512. }
  513. }
  514. private class BlockMaker extends AbstractMaker {
  515. public void startElement(Attributes attributes) {
  516. boolean isViewport = XMLUtil.getAttributeAsBoolean(attributes,
  517. "is-viewport-area", false);
  518. Block block;
  519. if (isViewport) {
  520. BlockViewport bv = new BlockViewport();
  521. bv.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clipped", false));
  522. bv.setCTM(getAttributeAsCTM(attributes, "ctm"));
  523. if (bv.getPositioning() != BlockViewport.RELATIVE) {
  524. bv.setXOffset(
  525. XMLUtil.getAttributeAsInt(attributes, "left-position", 0));
  526. bv.setYOffset(
  527. XMLUtil.getAttributeAsInt(attributes, "top-position", 0));
  528. }
  529. block = bv;
  530. } else {
  531. block = new Block();
  532. }
  533. String positioning = attributes.getValue("positioning");
  534. if ("absolute".equalsIgnoreCase(positioning)) {
  535. block.setPositioning(Block.ABSOLUTE);
  536. } else if ("fixed".equalsIgnoreCase(positioning)) {
  537. block.setPositioning(Block.FIXED);
  538. } else if ("relative".equalsIgnoreCase(positioning)) {
  539. block.setPositioning(Block.RELATIVE);
  540. } else {
  541. block.setPositioning(Block.STACK);
  542. }
  543. if (attributes.getValue("left-offset") != null) {
  544. block.setXOffset(XMLUtil.getAttributeAsInt(attributes, "left-offset", 0));
  545. }
  546. if (attributes.getValue("top-offset") != null) {
  547. block.setYOffset(XMLUtil.getAttributeAsInt(attributes, "top-offset", 0));
  548. }
  549. transferForeignObjects(attributes, block);
  550. setAreaAttributes(attributes, block);
  551. setTraits(attributes, block, SUBSET_COMMON);
  552. setTraits(attributes, block, SUBSET_BOX);
  553. setTraits(attributes, block, SUBSET_COLOR);
  554. Area parent = (Area)areaStack.peek();
  555. //BlockParent parent = getCurrentBlockParent();
  556. parent.addChildArea(block);
  557. areaStack.push(block);
  558. }
  559. public void endElement() {
  560. assertObjectOfClass(areaStack.pop(), Block.class);
  561. }
  562. }
  563. private class LineAreaMaker extends AbstractMaker {
  564. public void startElement(Attributes attributes) {
  565. LineArea line = new LineArea();
  566. setAreaAttributes(attributes, line);
  567. setTraits(attributes, line, SUBSET_COMMON);
  568. setTraits(attributes, line, SUBSET_BOX);
  569. setTraits(attributes, line, SUBSET_COLOR);
  570. BlockParent parent = getCurrentBlockParent();
  571. parent.addChildArea(line);
  572. areaStack.push(line);
  573. }
  574. public void endElement() {
  575. assertObjectOfClass(areaStack.pop(), LineArea.class);
  576. }
  577. }
  578. // Maker for "generic" inline areas
  579. private class InlineMaker extends AbstractMaker {
  580. public void startElement(Attributes attributes) {
  581. InlineArea inl = new InlineArea();
  582. transferForeignObjects(attributes, inl);
  583. inl.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  584. setAreaAttributes(attributes, inl);
  585. setTraits(attributes, inl, SUBSET_COMMON);
  586. setTraits(attributes, inl, SUBSET_BOX);
  587. setTraits(attributes, inl, SUBSET_COLOR);
  588. Area parent = (Area)areaStack.peek();
  589. parent.addChildArea(inl);
  590. areaStack.push(inl);
  591. }
  592. public void endElement() {
  593. assertObjectOfClass(areaStack.pop(), InlineArea.class);
  594. }
  595. }
  596. private class InlineParentMaker extends AbstractMaker {
  597. public void startElement(Attributes attributes) {
  598. InlineParent ip = new InlineParent();
  599. transferForeignObjects(attributes, ip);
  600. ip.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  601. setAreaAttributes(attributes, ip);
  602. setTraits(attributes, ip, SUBSET_COMMON);
  603. setTraits(attributes, ip, SUBSET_BOX);
  604. setTraits(attributes, ip, SUBSET_COLOR);
  605. setTraits(attributes, ip, SUBSET_LINK);
  606. Area parent = (Area)areaStack.peek();
  607. parent.addChildArea(ip);
  608. areaStack.push(ip);
  609. }
  610. public void endElement() {
  611. assertObjectOfClass(areaStack.pop(), InlineParent.class);
  612. }
  613. }
  614. private class InlineBlockParentMaker extends AbstractMaker {
  615. public void startElement(Attributes attributes) {
  616. InlineBlockParent ibp = new InlineBlockParent();
  617. transferForeignObjects(attributes, ibp);
  618. ibp.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  619. setAreaAttributes(attributes, ibp);
  620. setTraits(attributes, ibp, SUBSET_COMMON);
  621. setTraits(attributes, ibp, SUBSET_BOX);
  622. setTraits(attributes, ibp, SUBSET_COLOR);
  623. Area parent = (Area)areaStack.peek();
  624. parent.addChildArea(ibp);
  625. areaStack.push(ibp);
  626. }
  627. public void endElement() {
  628. assertObjectOfClass(areaStack.pop(), InlineBlockParent.class);
  629. }
  630. }
  631. private class TextMaker extends AbstractMaker {
  632. public void startElement(Attributes attributes) {
  633. if (getCurrentText() != null) {
  634. throw new IllegalStateException("Current Text must be null");
  635. }
  636. TextArea text = new TextArea();
  637. setAreaAttributes(attributes, text);
  638. setTraits(attributes, text, SUBSET_COMMON);
  639. setTraits(attributes, text, SUBSET_BOX);
  640. setTraits(attributes, text, SUBSET_COLOR);
  641. setTraits(attributes, text, SUBSET_FONT);
  642. text.setBaselineOffset(XMLUtil.getAttributeAsInt(attributes, "baseline", 0));
  643. text.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  644. text.setTextLetterSpaceAdjust(XMLUtil.getAttributeAsInt(attributes,
  645. "tlsadjust", 0));
  646. text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes,
  647. "twsadjust", 0));
  648. Area parent = (Area)areaStack.peek();
  649. parent.addChildArea(text);
  650. areaStack.push(text);
  651. }
  652. public void endElement() {
  653. assertObjectOfClass(areaStack.pop(), TextArea.class);
  654. }
  655. }
  656. private class WordMaker extends AbstractMaker {
  657. public void endElement() {
  658. int offset = XMLUtil.getAttributeAsInt(lastAttributes, "offset", 0);
  659. int[] letterAdjust
  660. = ConversionUtils.toIntArray(
  661. lastAttributes.getValue("letter-adjust"), "\\s");
  662. content.flip();
  663. WordArea word = new WordArea(content.toString().trim(), offset, letterAdjust);
  664. AbstractTextArea text = getCurrentText();
  665. word.setParentArea(text);
  666. text.addChildArea(word);
  667. }
  668. public boolean ignoreCharacters() {
  669. return false;
  670. }
  671. }
  672. private class SpaceMaker extends AbstractMaker {
  673. public void endElement() {
  674. int offset = XMLUtil.getAttributeAsInt(lastAttributes, "offset", 0);
  675. //TODO the isAdjustable parameter is currently not used/implemented
  676. if (content.position() > 0) {
  677. content.flip();
  678. boolean adjustable = XMLUtil.getAttributeAsBoolean(lastAttributes, "adj", true);
  679. SpaceArea space = new SpaceArea(content.charAt(0), offset, adjustable);
  680. AbstractTextArea text = getCurrentText();
  681. space.setParentArea(text);
  682. text.addChildArea(space);
  683. } else {
  684. Space space = new Space();
  685. setAreaAttributes(lastAttributes, space);
  686. setTraits(lastAttributes, space, SUBSET_COMMON);
  687. setTraits(lastAttributes, space, SUBSET_BOX);
  688. setTraits(lastAttributes, space, SUBSET_COLOR);
  689. space.setOffset(offset);
  690. Area parent = (Area)areaStack.peek();
  691. parent.addChildArea(space);
  692. }
  693. }
  694. public boolean ignoreCharacters() {
  695. return false;
  696. }
  697. }
  698. private class LeaderMaker extends AbstractMaker {
  699. public void startElement(Attributes attributes) {
  700. Leader leader = new Leader();
  701. transferForeignObjects(attributes, leader);
  702. setAreaAttributes(attributes, leader);
  703. setTraits(attributes, leader, SUBSET_COMMON);
  704. setTraits(attributes, leader, SUBSET_BOX);
  705. setTraits(attributes, leader, SUBSET_COLOR);
  706. setTraits(attributes, leader, SUBSET_FONT);
  707. leader.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  708. String ruleStyle = attributes.getValue("ruleStyle");
  709. if (ruleStyle != null) {
  710. leader.setRuleStyle(ruleStyle);
  711. }
  712. leader.setRuleThickness(
  713. XMLUtil.getAttributeAsInt(attributes, "ruleThickness", 0));
  714. Area parent = (Area)areaStack.peek();
  715. parent.addChildArea(leader);
  716. }
  717. }
  718. private class ViewportMaker extends AbstractMaker {
  719. public void startElement(Attributes attributes) {
  720. Viewport viewport = new Viewport(null);
  721. transferForeignObjects(attributes, viewport);
  722. setAreaAttributes(attributes, viewport);
  723. setTraits(attributes, viewport, SUBSET_COMMON);
  724. setTraits(attributes, viewport, SUBSET_BOX);
  725. setTraits(attributes, viewport, SUBSET_COLOR);
  726. viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos"));
  727. viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false));
  728. viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
  729. Area parent = (Area)areaStack.peek();
  730. parent.addChildArea(viewport);
  731. areaStack.push(viewport);
  732. }
  733. public void endElement() {
  734. assertObjectOfClass(areaStack.pop(), Viewport.class);
  735. }
  736. }
  737. private class ImageMaker extends AbstractMaker {
  738. public void startElement(Attributes attributes) {
  739. String url = attributes.getValue("url");
  740. Image image = new Image(url);
  741. transferForeignObjects(attributes, image);
  742. setAreaAttributes(attributes, image);
  743. setTraits(attributes, image, SUBSET_COMMON);
  744. getCurrentViewport().setContent(image);
  745. }
  746. }
  747. private class ForeignObjectMaker extends AbstractMaker {
  748. public void startElement(Attributes attributes) throws SAXException {
  749. String ns = attributes.getValue("ns");
  750. domImplementation
  751. = elementMappingRegistry.getDOMImplementationForNamespace(ns);
  752. if (domImplementation == null) {
  753. throw new SAXException("No DOMImplementation could be"
  754. + " identified to handle namespace: " + ns);
  755. }
  756. ForeignObject foreign = new ForeignObject(ns);
  757. transferForeignObjects(attributes, foreign);
  758. setAreaAttributes(attributes, foreign);
  759. setTraits(attributes, foreign, SUBSET_COMMON);
  760. getCurrentViewport().setContent(foreign);
  761. areaStack.push(foreign);
  762. }
  763. public void endElement() {
  764. assertObjectOfClass(areaStack.pop(), ForeignObject.class);
  765. }
  766. }
  767. private class BookmarkTreeMaker extends AbstractMaker {
  768. public void startElement(Attributes attributes) {
  769. BookmarkData bm = new BookmarkData();
  770. areaStack.push(bm);
  771. }
  772. public void endElement() {
  773. Object tos = areaStack.pop();
  774. assertObjectOfClass(tos, BookmarkData.class);
  775. treeModel.handleOffDocumentItem((BookmarkData) tos);
  776. // as long as the bookmark tree comes after the last PageViewport in the
  777. // area tree XML, we don't have to worry about resolved/unresolved. The
  778. // only resolution needed is the mapping of the pvKey to the PV instance.
  779. }
  780. }
  781. private class BookmarkMaker extends AbstractMaker {
  782. public void startElement(Attributes attributes) {
  783. String title = attributes.getValue("title");
  784. boolean showChildren = XMLUtil.getAttributeAsBoolean(
  785. attributes, "show-children", false);
  786. String[] linkdata
  787. = InternalLink.parseXMLAttribute(attributes.getValue("internal-link"));
  788. PageViewport pv = (PageViewport) pageViewportsByKey.get(linkdata[0]);
  789. BookmarkData bm = new BookmarkData(title, showChildren, pv, linkdata[1]);
  790. Object tos = areaStack.peek();
  791. if (tos instanceof BookmarkData) {
  792. BookmarkData parent = (BookmarkData) tos;
  793. parent.addSubData(bm);
  794. }
  795. areaStack.push(bm);
  796. }
  797. public void endElement() {
  798. assertObjectOfClass(areaStack.pop(), BookmarkData.class);
  799. }
  800. }
  801. private class DestinationMaker extends AbstractMaker {
  802. public void startElement(Attributes attributes) {
  803. String[] linkdata
  804. = InternalLink.parseXMLAttribute(lastAttributes.getValue("internal-link"));
  805. PageViewport pv = (PageViewport) pageViewportsByKey.get(linkdata[0]);
  806. DestinationData dest = new DestinationData(linkdata[1]);
  807. List pages = new java.util.ArrayList();
  808. pages.add(pv);
  809. dest.resolveIDRef(linkdata[1], pages);
  810. areaStack.push(dest);
  811. }
  812. public void endElement() {
  813. Object tos = areaStack.pop();
  814. assertObjectOfClass(tos, DestinationData.class);
  815. treeModel.handleOffDocumentItem((DestinationData) tos);
  816. }
  817. }
  818. // ====================================================================
  819. private void pushNewRegionReference(Attributes attributes, int side) {
  820. String regionName = attributes.getValue("name");
  821. RegionViewport rv = getCurrentRegionViewport();
  822. RegionReference reg = new RegionReference(side,
  823. regionName, rv);
  824. transferForeignObjects(attributes, reg);
  825. reg.setCTM(getAttributeAsCTM(attributes, "ctm"));
  826. setAreaAttributes(attributes, reg);
  827. setTraits(attributes, reg, SUBSET_BORDER_PADDING);
  828. rv.setRegionReference(reg);
  829. currentPageViewport.getPage().setRegionViewport(
  830. side, rv);
  831. areaStack.push(reg);
  832. }
  833. private void assertObjectOfClass(Object obj, Class clazz) {
  834. if (!clazz.isInstance(obj)) {
  835. throw new IllegalStateException("Object is not an instance of "
  836. + clazz.getName() + " but of " + obj.getClass().getName());
  837. }
  838. }
  839. /**
  840. * Handles objects created by "sub-parsers" that implement the ObjectSource interface.
  841. * An example of object handled here are ExtensionAttachments.
  842. * @param obj the Object to be handled.
  843. */
  844. protected void handleExternallyGeneratedObject(Object obj) {
  845. if (areaStack.size() == 0 && obj instanceof ExtensionAttachment) {
  846. ExtensionAttachment attachment = (ExtensionAttachment)obj;
  847. if (this.currentPageViewport == null) {
  848. this.treeModel.handleOffDocumentItem(
  849. new OffDocumentExtensionAttachment(attachment));
  850. } else {
  851. this.currentPageViewport.addExtensionAttachment(attachment);
  852. }
  853. } else {
  854. Object o = areaStack.peek();
  855. if (o instanceof AreaTreeObject && obj instanceof ExtensionAttachment) {
  856. AreaTreeObject ato = (AreaTreeObject)o;
  857. ExtensionAttachment attachment = (ExtensionAttachment)obj;
  858. ato.addExtensionAttachment(attachment);
  859. } else {
  860. log.warn("Don't know how to handle externally generated object: " + obj);
  861. }
  862. }
  863. }
  864. private void setAreaAttributes(Attributes attributes, Area area) {
  865. area.setIPD(Integer.parseInt(attributes.getValue("ipd")));
  866. area.setBPD(Integer.parseInt(attributes.getValue("bpd")));
  867. }
  868. private static final Object[] SUBSET_COMMON = new Object[] {
  869. Trait.PROD_ID};
  870. private static final Object[] SUBSET_LINK = new Object[] {
  871. Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK};
  872. private static final Object[] SUBSET_COLOR = new Object[] {
  873. Trait.BACKGROUND, Trait.COLOR};
  874. private static final Object[] SUBSET_FONT = new Object[] {
  875. Trait.FONT, Trait.FONT_SIZE, Trait.BLINK,
  876. Trait.OVERLINE, Trait.OVERLINE_COLOR,
  877. Trait.LINETHROUGH, Trait.LINETHROUGH_COLOR,
  878. Trait.UNDERLINE, Trait.UNDERLINE_COLOR};
  879. private static final Object[] SUBSET_BOX = new Object[] {
  880. Trait.BORDER_BEFORE, Trait.BORDER_AFTER, Trait.BORDER_START, Trait.BORDER_END,
  881. Trait.SPACE_BEFORE, Trait.SPACE_AFTER, Trait.SPACE_START, Trait.SPACE_END,
  882. Trait.PADDING_BEFORE, Trait.PADDING_AFTER, Trait.PADDING_START, Trait.PADDING_END,
  883. Trait.START_INDENT, Trait.END_INDENT,
  884. Trait.IS_REFERENCE_AREA, Trait.IS_VIEWPORT_AREA};
  885. private static final Object[] SUBSET_BORDER_PADDING = new Object[] {
  886. Trait.BORDER_BEFORE, Trait.BORDER_AFTER, Trait.BORDER_START, Trait.BORDER_END,
  887. Trait.PADDING_BEFORE, Trait.PADDING_AFTER, Trait.PADDING_START, Trait.PADDING_END};
  888. private void setTraits(Attributes attributes, Area area, Object[] traitSubset) {
  889. for (int i = traitSubset.length; --i >= 0;) {
  890. Object trait = traitSubset[i];
  891. String traitName = Trait.getTraitName(trait);
  892. String value = attributes.getValue(traitName);
  893. if (value != null) {
  894. Class cl = Trait.getTraitClass(trait);
  895. if (cl == Integer.class) {
  896. area.addTrait(trait, new Integer(value));
  897. } else if (cl == Boolean.class) {
  898. area.addTrait(trait, Boolean.valueOf(value));
  899. } else if (cl == String.class) {
  900. area.addTrait(trait, value);
  901. if (trait == Trait.PROD_ID
  902. && !idFirstsAssigned.contains(value)
  903. && currentPageViewport != null) {
  904. currentPageViewport.setFirstWithID(value);
  905. idFirstsAssigned.add(value);
  906. }
  907. } else if (cl == Color.class) {
  908. try {
  909. area.addTrait(trait, ColorUtil.parseColorString(this.userAgent, value));
  910. } catch (PropertyException e) {
  911. throw new IllegalArgumentException(e.getMessage());
  912. }
  913. } else if (cl == InternalLink.class) {
  914. area.addTrait(trait, new InternalLink(value));
  915. } else if (cl == Trait.ExternalLink.class) {
  916. area.addTrait(trait, Trait.ExternalLink.makeFromTraitValue(value));
  917. } else if (cl == Background.class) {
  918. Background bkg = new Background();
  919. try {
  920. Color col = ColorUtil.parseColorString(
  921. this.userAgent, attributes.getValue("bkg-color"));
  922. bkg.setColor(col);
  923. } catch (PropertyException e) {
  924. throw new IllegalArgumentException(e.getMessage());
  925. }
  926. String uri = attributes.getValue("bkg-img");
  927. if (uri != null) {
  928. bkg.setURL(uri);
  929. try {
  930. ImageManager manager = userAgent.getFactory().getImageManager();
  931. ImageSessionContext sessionContext
  932. = userAgent.getImageSessionContext();
  933. ImageInfo info = manager.getImageInfo(uri, sessionContext);
  934. bkg.setImageInfo(info);
  935. } catch (ImageException e) {
  936. ResourceEventProducer eventProducer
  937. = ResourceEventProducer.Provider.get(
  938. this.userAgent.getEventBroadcaster());
  939. eventProducer.imageError(this, uri, e, getLocator());
  940. } catch (FileNotFoundException fnfe) {
  941. ResourceEventProducer eventProducer
  942. = ResourceEventProducer.Provider.get(
  943. this.userAgent.getEventBroadcaster());
  944. eventProducer.imageNotFound(this, uri, fnfe, getLocator());
  945. } catch (IOException ioe) {
  946. ResourceEventProducer eventProducer
  947. = ResourceEventProducer.Provider.get(
  948. this.userAgent.getEventBroadcaster());
  949. eventProducer.imageIOError(this, uri, ioe, getLocator());
  950. }
  951. String repeat = attributes.getValue("bkg-repeat");
  952. if (repeat != null) {
  953. bkg.setRepeat(repeat);
  954. }
  955. bkg.setHoriz(XMLUtil.getAttributeAsInt(attributes,
  956. "bkg-horz-offset", 0));
  957. bkg.setVertical(XMLUtil.getAttributeAsInt(attributes,
  958. "bkg-vert-offset", 0));
  959. }
  960. area.addTrait(trait, bkg);
  961. } else if (cl == BorderProps.class) {
  962. area.addTrait(trait, BorderProps.valueOf(this.userAgent, value));
  963. }
  964. } else {
  965. if (trait == Trait.FONT) {
  966. String fontName = attributes.getValue("font-name");
  967. if (fontName != null) {
  968. String fontStyle = attributes.getValue("font-style");
  969. int fontWeight = XMLUtil.getAttributeAsInt(
  970. attributes, "font-weight", Font.WEIGHT_NORMAL);
  971. area.addTrait(trait,
  972. FontInfo.createFontKey(fontName, fontStyle, fontWeight));
  973. }
  974. }
  975. }
  976. }
  977. }
  978. private static CTM getAttributeAsCTM(Attributes attributes, String name) {
  979. String s = attributes.getValue(name).trim();
  980. if (s.startsWith("[") && s.endsWith("]")) {
  981. s = s.substring(1, s.length() - 1);
  982. double[] values = ConversionUtils.toDoubleArray(s, "\\s");
  983. if (values.length != 6) {
  984. throw new IllegalArgumentException("CTM must consist of 6 double values!");
  985. }
  986. return new CTM(values[0], values[1], values[2], values[3], values[4], values[5]);
  987. } else {
  988. throw new IllegalArgumentException("CTM must be surrounded by square brackets!");
  989. }
  990. }
  991. private static void transferForeignObjects(Attributes atts, AreaTreeObject ato) {
  992. for (int i = 0, c = atts.getLength(); i < c; i++) {
  993. String ns = atts.getURI(i);
  994. if (ns.length() > 0) {
  995. if ("http://www.w3.org/2000/xmlns/".equals(ns)) {
  996. continue;
  997. }
  998. QName qname = new QName(ns, atts.getQName(i));
  999. ato.setForeignAttribute(qname, atts.getValue(i));
  1000. }
  1001. }
  1002. }
  1003. /** {@inheritDoc} */
  1004. public void characters(char[] ch, int start, int length) throws SAXException {
  1005. if (delegate != null) {
  1006. delegate.characters(ch, start, length);
  1007. } else if (!ignoreCharacters) {
  1008. int maxLength = this.content.capacity() - this.content.position();
  1009. if (maxLength < length) {
  1010. // allocate a larger buffer and transfer content
  1011. CharBuffer newContent
  1012. = CharBuffer.allocate(this.content.position() + length);
  1013. this.content.flip();
  1014. newContent.put(this.content);
  1015. this.content = newContent;
  1016. }
  1017. // make sure the full capacity is used
  1018. this.content.limit(this.content.capacity());
  1019. // add characters to the buffer
  1020. this.content.put(ch, start, length);
  1021. // decrease the limit, if necessary
  1022. if (this.content.position() < this.content.limit()) {
  1023. this.content.limit(this.content.position());
  1024. }
  1025. }
  1026. }
  1027. }
  1028. }