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 52KB

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