git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_XGC_URI_Resolution@1387628 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_0
@@ -97,7 +97,7 @@ To review the archives, you have several options: | |||
<li>The <link href="http://marc.theaimsgroup.com/?l=fop-dev&r=1&w=2">Mailing list ARChives</link> (MARC) at the AIMS group (search).</li> | |||
<li><link href="http://www.mail-archive.com/fop-dev%40xmlgraphics.apache.org/">The Mail Archive</link>.</li> | |||
<li>The <link href="http://dir.gmane.org/gmane.text.xml.fop.devel">GMANE</link> archive.</li> | |||
<li>The <link href="http://www.nabble.com/FOP---Dev-f352.html">Nabble</link> archive.</li> | |||
<li>The <link href="http://apache-fop.1065347.n5.nabble.com/">Nabble</link> archive.</li> | |||
<li>The <link href="http://fop-dev.markmail.org">MarkMail</link> archive.</li> | |||
</ul> | |||
</li> |
@@ -43,17 +43,17 @@ If you are using Microsoft Outlook, this setting can be found at the "Mail Forma | |||
<title>Archives</title> | |||
<p>To review the archives, you have several options:</p> | |||
<ul> | |||
<li>The <link href="http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-users/">Apache Mailing List archive</link> (mod_mbox archive, no full-text search, yet).</li> | |||
<li>The <link href="http://xmlgraphics.apache.org/mail/fop-users/">Apache Mailing List archive</link> (gzipped mbox files).</li> | |||
<li>The <jump href="http://marc.theaimsgroup.com/?l=fop-user&r=1&w=2">Mailing list ARChives </jump> (MARC) at the AIMS group.</li> | |||
<li><jump href="http://www.mail-archive.com/fop-users%40xmlgraphics.apache.org/">The Mail Archive</jump>.</li> | |||
<li>The <jump href="http://dir.gmane.org/gmane.text.xml.fop.user">GMANE archive</jump>.</li> | |||
<li>The <jump href="http://www.nabble.com/FOP---Users-f353.html">Nabble archive</jump> (only posts after May 2005).</li> | |||
<li>The <jump href="http://apache-fop.1065347.n5.nabble.com/FOP-Users-f3.html">Nabble archive</jump> (only posts after May 2005).</li> | |||
<li>The <jump href="http://fop-users.markmail.org/">MarkMail archive</jump>.</li> | |||
</ul> | |||
<note> | |||
If you don't like mailing lists and prefer a forum-like system, have a look at | |||
If you don't like mailing lists and prefer a forum-like system, have a look at | |||
<jump href="http://dir.gmane.org/gmane.text.xml.fop.user">GMANE</jump> or | |||
<jump href="http://www.nabble.com/FOP---Users-f353.html">Nabble</jump>. They | |||
allow you to post to the mailing list without having to subscribe. |
@@ -971,7 +971,8 @@ Note that the value of the encoding attribute in the example is the double-byte | |||
xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="simple"> | |||
<afp:tag-logical-element name="The TLE Name" value="The TLE Value" /> | |||
<afp:tag-logical-element name="The TLE Name" value="The TLE Value" | |||
encoding="500" /> | |||
<fo:region-body/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
@@ -985,7 +986,7 @@ Note that the value of the encoding attribute in the example is the double-byte | |||
The tag-logical-element extension element can appear within a simple-page-master | |||
(page level) or it can appear as child of page-sequence (page group level). | |||
Multiple tag-logical-element extension elements within a simple-page-master or | |||
page-sequence are allowed. The name and value attributes are mandatory. | |||
page-sequence are allowed. The name and value attributes are mandatory. The encoding attribute specifying a CCSID encoding is optional. | |||
</p> | |||
</section> | |||
<section id="afp-no-operation"> | |||
@@ -1269,6 +1270,7 @@ Note that the value of the encoding attribute in the example is the double-byte | |||
<source><![CDATA[<renderer mime="image/tiff"> | |||
<transparent-page-background>true</transparent-page-background> | |||
<compression>CCITT T.6</compression> | |||
<single-strip>true</single-strip> | |||
<fonts><!-- described elsewhere --></fonts> | |||
</renderer>]]></source> | |||
<p> | |||
@@ -1302,6 +1304,10 @@ Note that the value of the encoding attribute in the example is the double-byte | |||
added separately. The internal TIFF codec from XML Graphics Commons only supports PackBits, | |||
Deflate and JPEG compression for writing. | |||
</note> | |||
<p> | |||
The default value for the <code>"single-strip"</code> is <code>"false"</code> resulting in the RowsPerStrip Tiff Tag equal to the number of rows. | |||
If set to <code>true</code> RowsPerStrip is set to 1. | |||
</p> | |||
</section> | |||
<section id="bitmap-rendering-options"> | |||
<title>Runtime Rendering Options</title> |
@@ -72,8 +72,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
private final Stack<FOEventHandler> converters = new Stack<FOEventHandler>(); | |||
private final Stack<FOEventRecorder> tableFooterRecorders = new Stack<FOEventRecorder>(); | |||
private final FOEventHandler structureTreeEventTrigger; | |||
/** The descendants of some elements like fo:leader must be ignored. */ | |||
@@ -165,6 +163,20 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
super.endPageNumberCitationLast(pageLast); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
handleStartArtifact(staticContent); | |||
converter.startStatic(staticContent); | |||
super.startStatic(staticContent); | |||
} | |||
@Override | |||
public void endStatic(StaticContent staticContent) { | |||
converter.endStatic(staticContent); | |||
handleEndArtifact(staticContent); | |||
super.endStatic(staticContent); | |||
} | |||
@Override | |||
public void startFlow(Flow fl) { | |||
converter.startFlow(fl); | |||
@@ -216,16 +228,11 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
@Override | |||
public void startTable(Table tbl) { | |||
converter.startTable(tbl); | |||
tableFooterRecorders.push(null); | |||
super.startTable(tbl); | |||
} | |||
@Override | |||
public void endTable(Table tbl) { | |||
FOEventRecorder tableFooterRecorder = tableFooterRecorders.pop(); | |||
if (tableFooterRecorder != null) { | |||
tableFooterRecorder.replay(converter); | |||
} | |||
converter.endTable(tbl); | |||
super.endTable(tbl); | |||
} | |||
@@ -256,8 +263,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
@Override | |||
public void startFooter(TableFooter footer) { | |||
converters.push(converter); | |||
converter = new FOEventRecorder(); | |||
converter.startFooter(footer); | |||
super.startFooter(footer); | |||
} | |||
@@ -265,10 +270,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
@Override | |||
public void endFooter(TableFooter footer) { | |||
converter.endFooter(footer); | |||
/* Replace the dummy table footer with the real one. */ | |||
tableFooterRecorders.pop(); | |||
tableFooterRecorders.push((FOEventRecorder) converter); | |||
converter = converters.pop(); | |||
super.endFooter(footer); | |||
} | |||
@@ -356,20 +357,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { | |||
super.endListBody(listItemBody); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
handleStartArtifact(staticContent); | |||
converter.startStatic(staticContent); | |||
super.startStatic(staticContent); | |||
} | |||
@Override | |||
public void endStatic(StaticContent statisContent) { | |||
converter.endStatic(statisContent); | |||
handleEndArtifact(statisContent); | |||
super.endStatic(statisContent); | |||
} | |||
@Override | |||
public void startMarkup() { | |||
converter.startMarkup(); |
@@ -1,508 +0,0 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.accessibility.fo; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.fop.fo.FOEventHandler; | |||
import org.apache.fop.fo.FOText; | |||
import org.apache.fop.fo.flow.BasicLink; | |||
import org.apache.fop.fo.flow.Block; | |||
import org.apache.fop.fo.flow.BlockContainer; | |||
import org.apache.fop.fo.flow.Character; | |||
import org.apache.fop.fo.flow.ExternalGraphic; | |||
import org.apache.fop.fo.flow.Footnote; | |||
import org.apache.fop.fo.flow.FootnoteBody; | |||
import org.apache.fop.fo.flow.Inline; | |||
import org.apache.fop.fo.flow.InstreamForeignObject; | |||
import org.apache.fop.fo.flow.Leader; | |||
import org.apache.fop.fo.flow.ListBlock; | |||
import org.apache.fop.fo.flow.ListItem; | |||
import org.apache.fop.fo.flow.ListItemBody; | |||
import org.apache.fop.fo.flow.ListItemLabel; | |||
import org.apache.fop.fo.flow.PageNumber; | |||
import org.apache.fop.fo.flow.PageNumberCitation; | |||
import org.apache.fop.fo.flow.PageNumberCitationLast; | |||
import org.apache.fop.fo.flow.Wrapper; | |||
import org.apache.fop.fo.flow.table.Table; | |||
import org.apache.fop.fo.flow.table.TableBody; | |||
import org.apache.fop.fo.flow.table.TableCell; | |||
import org.apache.fop.fo.flow.table.TableColumn; | |||
import org.apache.fop.fo.flow.table.TableFooter; | |||
import org.apache.fop.fo.flow.table.TableHeader; | |||
import org.apache.fop.fo.flow.table.TableRow; | |||
final class FOEventRecorder extends FOEventHandler { | |||
private interface Event { | |||
void replay(FOEventHandler target); | |||
} | |||
private final List<Event> events = new ArrayList<Event>(); | |||
public void replay(FOEventHandler target) { | |||
for (Event event : events) { | |||
event.replay(target); | |||
} | |||
} | |||
@Override | |||
public void startPageNumber(final PageNumber pagenum) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startPageNumber(pagenum); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endPageNumber(final PageNumber pagenum) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endPageNumber(pagenum); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startPageNumberCitation(final PageNumberCitation pageCite) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startPageNumberCitation(pageCite); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endPageNumberCitation(final PageNumberCitation pageCite) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endPageNumberCitation(pageCite); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startPageNumberCitationLast(final PageNumberCitationLast pageLast) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startPageNumberCitationLast(pageLast); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endPageNumberCitationLast(final PageNumberCitationLast pageLast) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endPageNumberCitationLast(pageLast); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startBlock(final Block bl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startBlock(bl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endBlock(final Block bl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endBlock(bl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startBlockContainer(final BlockContainer blc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startBlockContainer(blc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endBlockContainer(final BlockContainer blc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endBlockContainer(blc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startInline(final Inline inl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startInline(inl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endInline(final Inline inl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endInline(inl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startTable(final Table tbl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startTable(tbl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endTable(final Table tbl) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endTable(tbl); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startColumn(final TableColumn tc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startColumn(tc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endColumn(final TableColumn tc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endColumn(tc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startHeader(final TableHeader header) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startHeader(header); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endHeader(final TableHeader header) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endHeader(header); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startFooter(final TableFooter footer) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startFooter(footer); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endFooter(final TableFooter footer) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endFooter(footer); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startBody(final TableBody body) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startBody(body); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endBody(final TableBody body) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endBody(body); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startRow(final TableRow tr) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startRow(tr); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endRow(final TableRow tr) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endRow(tr); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startCell(final TableCell tc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startCell(tc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endCell(final TableCell tc) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endCell(tc); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startList(final ListBlock lb) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startList(lb); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endList(final ListBlock lb) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endList(lb); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startListItem(final ListItem li) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startListItem(li); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endListItem(final ListItem li) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endListItem(li); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startListLabel(final ListItemLabel listItemLabel) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startListLabel(listItemLabel); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endListLabel(final ListItemLabel listItemLabel) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endListLabel(listItemLabel); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startListBody(final ListItemBody listItemBody) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startListBody(listItemBody); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endListBody(final ListItemBody listItemBody) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endListBody(listItemBody); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startLink(final BasicLink basicLink) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startLink(basicLink); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endLink(final BasicLink basicLink) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endLink(basicLink); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void image(final ExternalGraphic eg) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.image(eg); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startInstreamForeignObject(final InstreamForeignObject ifo) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startInstreamForeignObject(ifo); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endInstreamForeignObject(final InstreamForeignObject ifo) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endInstreamForeignObject(ifo); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startFootnote(final Footnote footnote) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startFootnote(footnote); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endFootnote(final Footnote footnote) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endFootnote(footnote); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startFootnoteBody(final FootnoteBody body) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startFootnoteBody(body); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endFootnoteBody(final FootnoteBody body) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endFootnoteBody(body); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startLeader(final Leader l) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startLeader(l); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endLeader(final Leader l) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endLeader(l); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void startWrapper(final Wrapper wrapper) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.startWrapper(wrapper); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void endWrapper(final Wrapper wrapper) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.endWrapper(wrapper); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void character(final Character c) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.character(c); | |||
} | |||
}); | |||
} | |||
@Override | |||
public void characters(final FOText foText) { | |||
events.add(new Event() { | |||
public void replay(FOEventHandler target) { | |||
target.characters(foText); | |||
} | |||
}); | |||
} | |||
} |
@@ -55,6 +55,7 @@ import org.apache.fop.fo.flow.table.TableFooter; | |||
import org.apache.fop.fo.flow.table.TableHeader; | |||
import org.apache.fop.fo.flow.table.TableRow; | |||
import org.apache.fop.fo.pagination.Flow; | |||
import org.apache.fop.fo.pagination.LayoutMasterSet; | |||
import org.apache.fop.fo.pagination.PageSequence; | |||
import org.apache.fop.fo.pagination.StaticContent; | |||
import org.apache.fop.fo.properties.CommonAccessibilityHolder; | |||
@@ -67,6 +68,8 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
private StructureTreeEventHandler structureTreeEventHandler; | |||
private LayoutMasterSet layoutMasterSet; | |||
public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { | |||
this.structureTreeEventHandler = structureTreeEventHandler; | |||
} | |||
@@ -81,6 +84,9 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
@Override | |||
public void startPageSequence(PageSequence pageSeq) { | |||
if (layoutMasterSet == null) { | |||
layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet(); | |||
} | |||
Locale locale = null; | |||
if (pageSeq.getLanguage() != null) { | |||
if (pageSeq.getCountry() != null) { | |||
@@ -128,9 +134,28 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
endElement(pageLast); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
AttributesImpl flowName = createFlowNameAttribute(staticContent.getFlowName()); | |||
startElement(staticContent, flowName); | |||
} | |||
private AttributesImpl createFlowNameAttribute(String flowName) { | |||
String regionName = layoutMasterSet.getDefaultRegionNameFor(flowName); | |||
AttributesImpl attribute = new AttributesImpl(); | |||
addNoNamespaceAttribute(attribute, Flow.FLOW_NAME, regionName); | |||
return attribute; | |||
} | |||
@Override | |||
public void endStatic(StaticContent staticContent) { | |||
endElement(staticContent); | |||
} | |||
@Override | |||
public void startFlow(Flow fl) { | |||
startElement(fl); | |||
AttributesImpl flowName = createFlowNameAttribute(fl.getFlowName()); | |||
startElement(fl, flowName); | |||
} | |||
@Override | |||
@@ -277,16 +302,6 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
endElement(listItemBody); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
startElement(staticContent); | |||
} | |||
@Override | |||
public void endStatic(StaticContent statisContent) { | |||
endElement(statisContent); | |||
} | |||
@Override | |||
public void startLink(BasicLink basicLink) { | |||
startElementWithID(basicLink); |
@@ -113,4 +113,13 @@ public interface AFPEventProducer extends EventProducer { | |||
* @event.severity ERROR | |||
*/ | |||
void invalidConfiguration(Object source, Exception e); | |||
/** | |||
* The characterset is missing metric information for the specified character | |||
* @param source the event source | |||
* @param character the character with missing metric information. | |||
* @param charSet the character set containing missing metric information | |||
* @event.severity WARN | |||
*/ | |||
void charactersetMissingMetrics(Object source, char character, String charSet); | |||
} |
@@ -8,4 +8,5 @@ | |||
<message key="fontConfigMissing">The mandatory configuation node: '{missingConfig}' was not found at {location}.</message> | |||
<message key="characterSetNameInvalid">The character set given has an invalid name. [ Reason: {msg} ]</message> | |||
<message key="codePageNotFound">The code page for an AFP font cannot be found.[ Reason: {e}]</message> | |||
<message key="charactersetMissingMetrics">Metric information is missing for the glyph `{character}` in the characterset `{charSet}`. Using space increment width.</message> | |||
</catalogue> |
@@ -40,7 +40,7 @@ import org.apache.fop.afp.modca.Overlay; | |||
import org.apache.fop.afp.modca.PageGroup; | |||
import org.apache.fop.afp.modca.PageObject; | |||
import org.apache.fop.afp.modca.ResourceGroup; | |||
import org.apache.fop.afp.modca.TagLogicalElementBean; | |||
import org.apache.fop.afp.modca.TagLogicalElement; | |||
import org.apache.fop.afp.modca.triplets.FullyQualifiedNameTriplet; | |||
import org.apache.fop.afp.ptoca.PtocaBuilder; | |||
import org.apache.fop.afp.ptoca.PtocaProducer; | |||
@@ -85,12 +85,9 @@ public class DataStream { | |||
/** The current page */ | |||
private AbstractPageObject currentPage = null; | |||
/** Sequence number for TLE's.*/ | |||
private int tleSequence = 0; | |||
/** The MO:DCA interchange set in use (default to MO:DCA-P IS/2 set) */ | |||
private InterchangeSet interchangeSet | |||
= InterchangeSet.valueOf(InterchangeSet.MODCA_PRESENTATION_INTERCHANGE_SET_2); | |||
= InterchangeSet.valueOf(InterchangeSet.MODCA_PRESENTATION_INTERCHANGE_SET_2); | |||
private final Factory factory; | |||
@@ -544,17 +541,19 @@ public class DataStream { | |||
currentPage.createIncludePageSegment(name, xOrigin, yOrigin, createHardPageSegments); | |||
} | |||
/** | |||
* Creates a TagLogicalElement on the current page. | |||
* | |||
* @param attributes | |||
* the array of key value pairs. | |||
*/ | |||
public void createPageTagLogicalElement(TagLogicalElementBean[] attributes) { | |||
public void createPageTagLogicalElement(TagLogicalElement.State[] attributes) { | |||
for (int i = 0; i < attributes.length; i++) { | |||
String name = attributes[i].getKey(); | |||
String value = attributes[i].getValue(); | |||
currentPage.createTagLogicalElement(name, value, tleSequence++); | |||
currentPage.createTagLogicalElement(attributes[i]); | |||
} | |||
} | |||
@@ -564,11 +563,9 @@ public class DataStream { | |||
* @param attributes | |||
* the array of key value pairs. | |||
*/ | |||
public void createPageGroupTagLogicalElement(TagLogicalElementBean[] attributes) { | |||
public void createPageGroupTagLogicalElement(TagLogicalElement.State[] attributes) { | |||
for (int i = 0; i < attributes.length; i++) { | |||
String name = attributes[i].getKey(); | |||
String value = attributes[i].getValue(); | |||
currentPageGroup.createTagLogicalElement(name, value); | |||
currentPageGroup.createTagLogicalElement(attributes[i]); | |||
} | |||
} | |||
@@ -579,12 +576,17 @@ public class DataStream { | |||
* The tag name | |||
* @param value | |||
* The tag value | |||
* @param encoding The CCSID character set encoding | |||
*/ | |||
public void createTagLogicalElement(String name, String value) { | |||
public void createTagLogicalElement(String name, String value, int encoding) { | |||
TagLogicalElement.State tleState = new TagLogicalElement.State(name, value, encoding); | |||
if (currentPage != null) { | |||
currentPage.createTagLogicalElement(name, value, tleSequence++); | |||
currentPage.createTagLogicalElement(tleState); | |||
} else { | |||
currentPageGroup.createTagLogicalElement(name, value); | |||
currentPageGroup.createTagLogicalElement(tleState); | |||
} | |||
} | |||
@@ -632,7 +634,7 @@ public class DataStream { | |||
*/ | |||
public void startPageGroup() throws IOException { | |||
endPageGroup(); | |||
this.currentPageGroup = factory.createPageGroup(tleSequence); | |||
this.currentPageGroup = factory.createPageGroup(); | |||
} | |||
/** | |||
@@ -643,7 +645,6 @@ public class DataStream { | |||
public void endPageGroup() throws IOException { | |||
if (currentPageGroup != null) { | |||
currentPageGroup.endPageGroup(); | |||
tleSequence = currentPageGroup.getTleSequence(); | |||
document.addPageGroup(currentPageGroup); | |||
currentPageGroup = null; | |||
} |
@@ -214,13 +214,12 @@ public class Factory { | |||
/** | |||
* Creates a new MO:DCA {@link PageGroup} | |||
* @param tleSequence current start tle sequence number within stream | |||
* @return a new {@link PageGroup} | |||
*/ | |||
public PageGroup createPageGroup(int tleSequence) { | |||
public PageGroup createPageGroup() { | |||
String name = PAGE_GROUP_NAME_PREFIX | |||
+ StringUtils.lpad(String.valueOf(++pageGroupCount), '0', 5); | |||
return new PageGroup(this, name, tleSequence); | |||
return new PageGroup(this, name); | |||
} | |||
/** | |||
@@ -374,13 +373,11 @@ public class Factory { | |||
/** | |||
* Creates a MO:DCA {@link TagLogicalElement} | |||
* | |||
* @param name name of the element | |||
* @param value value of the element | |||
* @param tleSequence current start tle sequence number within stream* | |||
* @param state the attribute state for the TLE | |||
* @return a new {@link TagLogicalElement} | |||
*/ | |||
public TagLogicalElement createTagLogicalElement(String name, String value, int tleSequence) { | |||
TagLogicalElement tle = new TagLogicalElement(name, value, tleSequence); | |||
public TagLogicalElement createTagLogicalElement(TagLogicalElement.State state) { | |||
TagLogicalElement tle = new TagLogicalElement(state); | |||
return tle; | |||
} | |||
@@ -19,6 +19,8 @@ | |||
package org.apache.fop.afp.fonts; | |||
import org.apache.fop.afp.AFPEventProducer; | |||
/** | |||
* A font defined as a set of lines and curves as opposed to a bitmap font. An | |||
* outline font can be scaled to any size and otherwise transformed more easily | |||
@@ -29,16 +31,25 @@ public abstract class AbstractOutlineFont extends AFPFont { | |||
/** The character set for this font */ | |||
protected CharacterSet charSet = null; | |||
private final AFPEventProducer eventProducer; | |||
/** | |||
* Constructor for an outline font. | |||
* | |||
* @param name the name of the font | |||
* @param embeddable sets whether or not this font is to be embedded | |||
* @param charSet the chracter set | |||
* @param eventProducer The object to handle any events which occur from the object. | |||
*/ | |||
public AbstractOutlineFont(String name, boolean embeddable, CharacterSet charSet) { | |||
public AbstractOutlineFont(String name, boolean embeddable, CharacterSet charSet, | |||
AFPEventProducer eventProducer) { | |||
super(name, embeddable); | |||
this.charSet = charSet; | |||
this.eventProducer = eventProducer; | |||
} | |||
AFPEventProducer getAFPEventProducer() { | |||
return eventProducer; | |||
} | |||
/** |
@@ -363,4 +363,12 @@ public class CharacterSet { | |||
return getCharacterSetOrientation().getEmSpaceIncrement(); | |||
} | |||
/** | |||
* Returns the nominal character increment. | |||
* @return the nominal character increment | |||
*/ | |||
public int getNominalCharIncrement() { | |||
return getCharacterSetOrientation().getNominalCharIncrement(); | |||
} | |||
} |
@@ -236,9 +236,9 @@ public abstract class CharacterSetBuilder { | |||
CharacterSetType charsetType, AFPResourceAccessor accessor, AFPEventProducer eventProducer) | |||
throws IOException { | |||
// check for cached version of the characterset | |||
String descriptor = characterSetName + "_" + encoding + "_" + codePageName; | |||
CharacterSet characterSet = (CharacterSet) characterSetsCache.get(descriptor); | |||
URI charSetURI = accessor.resolveURI(characterSetName); | |||
String cacheKey = charSetURI.toASCIIString() + "_" + characterSetName + "_" + codePageName; | |||
CharacterSet characterSet = (CharacterSet) characterSetsCache.get(cacheKey); | |||
if (characterSet != null) { | |||
return characterSet; | |||
} | |||
@@ -257,6 +257,8 @@ public abstract class CharacterSetBuilder { | |||
* chracter global identifier. | |||
*/ | |||
Map<String, String> codePage; | |||
// TODO: This could have performance implications if several threads want to use the | |||
// codePagesCache to retrieve different codepages. | |||
synchronized (codePagesCache) { | |||
codePage = codePagesCache.get(codePageName); | |||
@@ -308,7 +310,7 @@ public abstract class CharacterSetBuilder { | |||
} finally { | |||
closeInputStream(inputStream); | |||
} | |||
characterSetsCache.put(descriptor, characterSet); | |||
characterSetsCache.put(cacheKey, characterSet); | |||
return characterSet; | |||
} | |||
@@ -446,20 +448,15 @@ public abstract class CharacterSetBuilder { | |||
position++; | |||
if (position == 26) { | |||
position = 0; | |||
int orientation = determineOrientation(fnoData[2]); | |||
// Space Increment | |||
int space = ((fnoData[8] & 0xFF ) << 8) + (fnoData[9] & 0xFF); | |||
// Em-Space Increment | |||
int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF); | |||
CharacterSetOrientation cso = new CharacterSetOrientation(orientation); | |||
cso.setSpaceIncrement(space); | |||
cso.setEmSpaceIncrement(em); | |||
orientations.add(cso); | |||
int spaceIncrement = getUBIN(fnoData, 8); | |||
int emIncrement = getUBIN(fnoData, 14); | |||
int nominalCharacterIncrement = getUBIN(fnoData, 20); | |||
orientations.add(new CharacterSetOrientation(orientation, spaceIncrement, | |||
emIncrement, nominalCharacterIncrement)); | |||
} | |||
} | |||
return orientations.toArray(EMPTY_CSO_ARRAY); |
@@ -60,7 +60,7 @@ public class CharacterSetOrientation { | |||
/** | |||
* The character widths in the character set (indexed using Unicode codepoints) | |||
*/ | |||
private int[] charsWidths = null; | |||
private int[] charsWidths; | |||
/** | |||
* The height of lowercase letters | |||
@@ -77,25 +77,26 @@ public class CharacterSetOrientation { | |||
*/ | |||
private char lastChar; | |||
/** | |||
* The character set orientation | |||
*/ | |||
private int orientation = 0; | |||
/** The character set orientation */ | |||
private final int orientation; | |||
/** space increment */ | |||
private int spaceIncrement; | |||
private final int spaceIncrement; | |||
/** em space increment */ | |||
private int emSpaceIncrement = -1; | |||
private final int emSpaceIncrement; | |||
/** Nominal Character Increment */ | |||
private final int nomCharIncrement; | |||
/** | |||
* Constructor for the CharacterSetOrientation, the orientation is | |||
* expressed as the degrees rotation (i.e 0, 90, 180, 270) | |||
* @param orientation the character set orientation | |||
*/ | |||
public CharacterSetOrientation(int orientation) { | |||
public CharacterSetOrientation(int orientation, int spaceIncrement, int emSpaceIncrement, | |||
int nomCharIncrement) { | |||
this.orientation = orientation; | |||
this.spaceIncrement = spaceIncrement; | |||
this.emSpaceIncrement = emSpaceIncrement; | |||
this.nomCharIncrement = nomCharIncrement; | |||
charsWidths = new int[256]; | |||
Arrays.fill(charsWidths, -1); | |||
} | |||
@@ -283,14 +284,6 @@ public class CharacterSetOrientation { | |||
return this.spaceIncrement; | |||
} | |||
/** | |||
* Sets the space increment. | |||
* @param value the space increment | |||
*/ | |||
public void setSpaceIncrement(int value) { | |||
this.spaceIncrement = value; | |||
} | |||
/** | |||
* Returns the em space increment. | |||
* @return the em space increment | |||
@@ -300,11 +293,10 @@ public class CharacterSetOrientation { | |||
} | |||
/** | |||
* Sets the em space increment. | |||
* @param value the em space increment | |||
* Returns the nominal character increment. | |||
* @return the nominal character increment | |||
*/ | |||
public void setEmSpaceIncrement(int value) { | |||
this.emSpaceIncrement = value; | |||
public int getNominalCharIncrement() { | |||
return this.nomCharIncrement; | |||
} | |||
} |
@@ -23,6 +23,11 @@ import java.lang.Character.UnicodeBlock; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.afp.AFPEventProducer; | |||
/** | |||
* Implementation of AbstractOutlineFont that supports double-byte fonts (CID Keyed font (Type 0)). | |||
* The width of characters that are not prescribed a width metrics in the font resource use | |||
@@ -31,7 +36,9 @@ import java.util.Set; | |||
*/ | |||
public class DoubleByteFont extends AbstractOutlineFont { | |||
//private static final Log LOG = LogFactory.getLog(DoubleByteFont.class); | |||
private static final Log log = LogFactory.getLog(DoubleByteFont.class); | |||
private final Set<Integer> charsProcessed; | |||
//See also http://unicode.org/reports/tr11/ which we've not closely looked at, yet | |||
//TODO the Unicode block listed here is probably not complete (ex. Hiragana, Katakana etc.) | |||
@@ -49,9 +56,12 @@ public class DoubleByteFont extends AbstractOutlineFont { | |||
* @param name the name of the font | |||
* @param embeddable whether or not this font is embeddable | |||
* @param charSet the character set | |||
* @param eventProducer Handles any AFP related events | |||
*/ | |||
public DoubleByteFont(String name, boolean embeddable, CharacterSet charSet) { | |||
super(name, embeddable, charSet); | |||
public DoubleByteFont(String name, boolean embeddable, CharacterSet charSet, | |||
AFPEventProducer eventProducer) { | |||
super(name, embeddable, charSet, eventProducer); | |||
charsProcessed = new HashSet<Integer>(); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -60,16 +70,30 @@ public class DoubleByteFont extends AbstractOutlineFont { | |||
try { | |||
charWidth = charSet.getWidth(toUnicodeCodepoint(character)); | |||
} catch (IllegalArgumentException e) { | |||
if (!charsProcessed.contains(character)) { | |||
charsProcessed.add(character); | |||
getAFPEventProducer().charactersetMissingMetrics(this, (char)character, | |||
charSet.getName().trim()); | |||
} | |||
// We shall try and handle characters that have no mapped width metric in font resource | |||
charWidth = -1; | |||
} | |||
if (charWidth == -1) { | |||
charWidth = inferCharWidth(character); | |||
charWidth = getDefaultCharacterWidth(character); | |||
} | |||
return charWidth * size; | |||
} | |||
private int getDefaultCharacterWidth(int character) { | |||
int nominalCharIncrement = charSet.getNominalCharIncrement(); | |||
if (nominalCharIncrement > 0) { | |||
return nominalCharIncrement; | |||
} else { | |||
return inferCharWidth(character); | |||
} | |||
} | |||
private int inferCharWidth(int character) { | |||
//Is this character an ideograph? |
@@ -19,6 +19,8 @@ | |||
package org.apache.fop.afp.fonts; | |||
import org.apache.fop.afp.AFPEventProducer; | |||
/** | |||
* Default implementation of AbstractOutlineFont. | |||
*/ | |||
@@ -29,9 +31,11 @@ public class OutlineFont extends AbstractOutlineFont { | |||
* @param name font's name | |||
* @param embeddable whether or not this font is embeddable | |||
* @param charSet font's character set | |||
* @param eventProducer Handles any AFP related events | |||
*/ | |||
public OutlineFont(String name, boolean embeddable, CharacterSet charSet) { | |||
super(name, embeddable, charSet); | |||
public OutlineFont(String name, boolean embeddable, CharacterSet charSet, | |||
AFPEventProducer eventProducer) { | |||
super(name, embeddable, charSet, eventProducer); | |||
} | |||
} |
@@ -223,19 +223,15 @@ public abstract class AbstractPageObject extends AbstractNamedAFPObject implemen | |||
/** | |||
* Creates a TagLogicalElement on the page. | |||
* | |||
* @param name | |||
* the name of the tag | |||
* @param value | |||
* the value of the tag | |||
* @param tleID | |||
* unique ID within AFP stream | |||
* @param state the state of the TLE | |||
*/ | |||
public void createTagLogicalElement(String name, String value, int tleID) { | |||
TagLogicalElement tle = new TagLogicalElement(name, value, tleID); | |||
public void createTagLogicalElement(TagLogicalElement.State state) { | |||
TagLogicalElement tle = new TagLogicalElement(state); | |||
List list = getTagLogicalElements(); | |||
list.add(tle); | |||
} | |||
/** | |||
* Creates a NoOperation on the page. | |||
* |
@@ -34,7 +34,7 @@ import org.apache.fop.afp.util.AFPResourceUtil; | |||
*/ | |||
public class IncludedResourceObject extends AbstractNamedAFPObject { | |||
private AFPResourceAccessor resourceAccessor; | |||
private final AFPResourceAccessor resourceAccessor; | |||
private URI uri; | |||
/** |
@@ -35,36 +35,26 @@ import org.apache.fop.afp.Factory; | |||
*/ | |||
public class PageGroup extends AbstractResourceEnvironmentGroupContainer { | |||
/** | |||
* Sequence number for TLE's. | |||
*/ | |||
private int tleSequence = 0; | |||
/** | |||
* Constructor for the PageGroup. | |||
* | |||
* @param factory the resource manager | |||
* @param name the name of the page group | |||
* @param tleSequence current start tle sequence number within stream | |||
*/ | |||
public PageGroup(Factory factory, String name, int tleSequence) { | |||
public PageGroup(Factory factory, String name) { | |||
super(factory, name); | |||
this.tleSequence = tleSequence; | |||
} | |||
/** | |||
* Creates a TagLogicalElement on the page. | |||
* | |||
* @param name | |||
* the name of the tag | |||
* @param value | |||
* the value of the tag | |||
* @param state | |||
* the state of the TLE | |||
*/ | |||
public void createTagLogicalElement(String name, String value) { | |||
TagLogicalElement tle = factory.createTagLogicalElement(name, value, tleSequence); | |||
public void createTagLogicalElement(TagLogicalElement.State state) { | |||
TagLogicalElement tle = factory.createTagLogicalElement(state); | |||
if (!getTagLogicalElements().contains(tle)) { | |||
getTagLogicalElements().add(tle); | |||
tleSequence++; | |||
} | |||
} | |||
@@ -93,9 +83,4 @@ public class PageGroup extends AbstractResourceEnvironmentGroupContainer { | |||
public String toString() { | |||
return this.getName(); | |||
} | |||
/** @return the TLE sequence number */ | |||
public int getTleSequence() { | |||
return tleSequence; | |||
} | |||
} |
@@ -24,6 +24,7 @@ import java.io.OutputStream; | |||
import org.apache.fop.afp.modca.triplets.AttributeQualifierTriplet; | |||
import org.apache.fop.afp.modca.triplets.AttributeValueTriplet; | |||
import org.apache.fop.afp.modca.triplets.EncodingTriplet; | |||
import org.apache.fop.afp.modca.triplets.FullyQualifiedNameTriplet; | |||
import org.apache.fop.afp.util.BinaryUtils; | |||
@@ -49,42 +50,30 @@ import org.apache.fop.afp.util.BinaryUtils; | |||
public class TagLogicalElement extends AbstractTripletStructuredObject { | |||
/** | |||
* Name of the key, used within the TLE | |||
* the params of the TLE | |||
*/ | |||
private String name = null; | |||
/** | |||
* Value returned by the key | |||
*/ | |||
private String value = null; | |||
/** | |||
* Sequence of TLE within document | |||
*/ | |||
private int tleID; | |||
private State state; | |||
/** | |||
* Construct a tag logical element with the name and value specified. | |||
* | |||
* @param name the name of the tag logical element | |||
* @param value the value of the tag logical element | |||
* @param tleID unique identifier for TLE within AFP stream | |||
* @param state the state of the tag logical element | |||
*/ | |||
public TagLogicalElement(String name, String value, int tleID) { | |||
this.name = name; | |||
this.value = value; | |||
this.tleID = tleID; | |||
public TagLogicalElement(State state) { | |||
this.state = state; | |||
} | |||
/** | |||
* Sets the attribute value of this structured field | |||
* | |||
* @param value the attribute value | |||
*/ | |||
public void setAttributeValue(String value) { | |||
private void setAttributeValue(String value) { | |||
addTriplet(new AttributeValueTriplet(value)); | |||
} | |||
private void setEncoding(int encoding) { | |||
if (encoding != State.ENCODING_NONE) { | |||
addTriplet(new EncodingTriplet(encoding)); | |||
} | |||
} | |||
/** | |||
* Sets the attribute qualifier of this structured field | |||
* | |||
@@ -100,9 +89,9 @@ public class TagLogicalElement extends AbstractTripletStructuredObject { | |||
setFullyQualifiedName( | |||
FullyQualifiedNameTriplet.TYPE_ATTRIBUTE_GID, | |||
FullyQualifiedNameTriplet.FORMAT_CHARSTR, | |||
name); | |||
setAttributeValue(value); | |||
setAttributeQualifier(tleID, 1); | |||
state.key); | |||
setAttributeValue(state.value); | |||
setEncoding(state.encoding); | |||
byte[] data = new byte[SF_HEADER_LENGTH]; | |||
copySF(data, Type.ATTRIBUTE, Category.PROCESS_ELEMENT); | |||
@@ -115,4 +104,51 @@ public class TagLogicalElement extends AbstractTripletStructuredObject { | |||
writeTriplets(os); | |||
} | |||
/** | |||
* | |||
* Holds the attribute state of a TLE | |||
* | |||
*/ | |||
public static class State { | |||
/** | |||
* value interpreted as no encoding | |||
*/ | |||
public static final int ENCODING_NONE = -1; | |||
/** The key attribute */ | |||
private String key; | |||
/** The value attribute */ | |||
private String value; | |||
/** The CCSID character et encoding attribute */ | |||
private int encoding = ENCODING_NONE; | |||
/** | |||
* Constructor | |||
* | |||
* @param key the key attribute | |||
* @param value the value attribute | |||
*/ | |||
public State(String key, String value) { | |||
this.key = key; | |||
this.value = value; | |||
} | |||
/** | |||
* | |||
* @param key the key attribute | |||
* @param value the value attribute | |||
* @param encoding the CCSID character set encoding attribute | |||
*/ | |||
public State(String key, String value, int encoding) { | |||
this.key = key; | |||
this.value = value; | |||
this.encoding = encoding; | |||
} | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.afp.modca.triplets; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import org.apache.fop.afp.util.BinaryUtils; | |||
/** | |||
* | |||
* Represents a CCSID encoding triplet. | |||
* | |||
*/ | |||
public class EncodingTriplet extends AbstractTriplet { | |||
private int encoding; | |||
/** | |||
* @param encoding the CCSID character set encoding | |||
*/ | |||
public EncodingTriplet( int encoding) { | |||
super(CODED_GRAPHIC_CHARACTER_SET_GLOBAL_IDENTIFIER); | |||
this.encoding = encoding; | |||
} | |||
/** {@inheritDoc} */ | |||
public void writeToStream(OutputStream os) throws IOException { | |||
// [len,id,0,0,0,0] | |||
byte[] data = getData(); | |||
byte[] encodingBytes = BinaryUtils.convert(encoding, 2); | |||
// [len,id,0,0,0,0] -> [len.id,0,0,encodingBytes[0],encodingBytes[1]] | |||
System.arraycopy(encodingBytes, 0, data, 4, encodingBytes.length); | |||
os.write(data); | |||
} | |||
/** {@inheritDoc} */ | |||
public int getDataLength() { | |||
//len(1b) + id(1b) + 0x0000 (2b) + encoding (2b) = 6b | |||
return 6; | |||
} | |||
} |
@@ -24,6 +24,9 @@ import java.io.InputStream; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
/** | |||
@@ -31,8 +34,11 @@ import org.apache.fop.apps.io.InternalResourceResolver; | |||
*/ | |||
public final class AFPResourceAccessor { | |||
private static final Log log = LogFactory.getLog(AFPResourceAccessor.class); | |||
private final InternalResourceResolver resourceResolver; | |||
private final String baseURI; | |||
private final URI baseURI; | |||
private final URIResolver uriResolver; | |||
/** | |||
* Constructor for resource to be accessed via the {@link org.apache.fop.apps.FOUserAgent}. This | |||
@@ -45,7 +51,23 @@ public final class AFPResourceAccessor { | |||
*/ | |||
public AFPResourceAccessor(InternalResourceResolver resourceResolver, String baseURI) { | |||
this.resourceResolver = resourceResolver; | |||
this.baseURI = baseURI; | |||
URI actualBaseURI = null; | |||
URIResolver uriResolver; | |||
if (baseURI == null) { | |||
actualBaseURI = null; | |||
uriResolver = new NullBaseURIResolver(); | |||
} else { | |||
try { | |||
actualBaseURI = InternalResourceResolver.getBaseURI(baseURI); | |||
uriResolver = new BaseURIResolver(); | |||
} catch (URISyntaxException use) { | |||
log.error("The URI given \"" + baseURI + "\" is invalid: " + use.getMessage()); | |||
actualBaseURI = null; | |||
uriResolver = new NullBaseURIResolver(); | |||
} | |||
} | |||
this.baseURI = actualBaseURI; | |||
this.uriResolver = uriResolver; | |||
} | |||
/** | |||
@@ -57,18 +79,6 @@ public final class AFPResourceAccessor { | |||
this(resourceResolver, null); | |||
} | |||
private URI getResourceURI(URI uri) { | |||
if (baseURI == null) { | |||
return uri; | |||
} | |||
try { | |||
URI baseURI = InternalResourceResolver.getBaseURI(this.baseURI); | |||
return baseURI.resolve(uri); | |||
} catch (URISyntaxException use) { | |||
return uri; | |||
} | |||
} | |||
/** | |||
* Creates an {@link InputStream} given a URI. | |||
* | |||
@@ -77,6 +87,44 @@ public final class AFPResourceAccessor { | |||
* @throws IOException if an I/O error occurs while creating the InputStream. | |||
*/ | |||
public InputStream createInputStream(URI uri) throws IOException { | |||
return resourceResolver.getResource(getResourceURI(uri)); | |||
return resourceResolver.getResource(uriResolver.resolveURI(uri)); | |||
} | |||
/** | |||
* Returns the resolved URI, given the URI of a resource. | |||
* | |||
* @param uri the resource URI | |||
* @return the resolved URI | |||
*/ | |||
public URI resolveURI(String uri) { | |||
return uriResolver.resolveURI(uri); | |||
} | |||
private interface URIResolver { | |||
URI resolveURI(URI uri); | |||
URI resolveURI(String uri); | |||
} | |||
private static final class NullBaseURIResolver implements URIResolver { | |||
public URI resolveURI(URI uri) { | |||
return uri; | |||
} | |||
public URI resolveURI(String uri) { | |||
return URI.create("./" + uri.trim()); | |||
} | |||
} | |||
private final class BaseURIResolver implements URIResolver { | |||
public URI resolveURI(URI uri) { | |||
return baseURI.resolve(uri); | |||
} | |||
public URI resolveURI(String uri) { | |||
return baseURI.resolve(uri.trim()); | |||
} | |||
} | |||
} |
@@ -83,6 +83,15 @@ public final class ResourceResolverFactory { | |||
return new TempAwareResourceResolver(tempResourceResolver, defaultResourceResolver); | |||
} | |||
/** | |||
* This creates the builder class for binding URI schemas to implementations of | |||
* {@link ResourceResolver}. This allows users to define their own URI schemas such that they | |||
* have finer control over the acquisition of resources. | |||
* | |||
* @param defaultResolver the default resource resolver that should be used in the event that | |||
* none of the other registered resolvers match the schema | |||
* @return the schema aware {@link ResourceResolver} builder | |||
*/ | |||
public static SchemaAwareResourceResolverBuilder createSchemaAwareResourceResolverBuilder( | |||
ResourceResolver defaultResolver) { | |||
return new SchemaAwareResourceResolverBuilderImpl(defaultResolver); | |||
@@ -99,10 +108,12 @@ public final class ResourceResolverFactory { | |||
new NormalResourceResolver()); | |||
} | |||
/** {@inheritDoc} */ | |||
public Resource getResource(URI uri) throws IOException { | |||
return delegate.getResource(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(URI uri) throws IOException { | |||
return delegate.getOutputStream(uri); | |||
} | |||
@@ -125,6 +136,7 @@ public final class ResourceResolverFactory { | |||
return TempResourceURIGenerator.isTempUri(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public Resource getResource(URI uri) throws IOException { | |||
if (isTempUri(uri)) { | |||
return tempResourceResolver.getResource(uri.getPath()); | |||
@@ -133,6 +145,7 @@ public final class ResourceResolverFactory { | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(URI uri) throws IOException { | |||
if (isTempUri(uri)) { | |||
return tempResourceResolver.getOutputStream(uri.getPath()); | |||
@@ -140,7 +153,6 @@ public final class ResourceResolverFactory { | |||
return defaultResourceResolver.getOutputStream(uri); | |||
} | |||
} | |||
} | |||
private static class DefaultTempResourceResolver implements TempResourceResolver { | |||
@@ -150,10 +162,12 @@ public final class ResourceResolverFactory { | |||
return file; | |||
} | |||
/** {@inheritDoc} */ | |||
public Resource getResource(String id) throws IOException { | |||
return new Resource(getTempFile(id).toURI().toURL().openStream()); | |||
} | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(String id) throws IOException { | |||
File file = getTempFile(id); | |||
if (file.createNewFile()) { | |||
@@ -196,19 +210,52 @@ public final class ResourceResolverFactory { | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public Resource getResource(URI uri) throws IOException { | |||
return getResourceResolverForSchema(uri).getResource(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(URI uri) throws IOException { | |||
return getResourceResolverForSchema(uri).getOutputStream(uri); | |||
} | |||
} | |||
/** | |||
* Implementations of this interface will be builders for {@link ResourceResolver}, they bind | |||
* URI schemas to their respective resolver. This gives users more control over the mechanisms | |||
* by which URIs are resolved. | |||
* <p> | |||
* Here is an example of how this could be used: | |||
* </p> | |||
* <p><code> | |||
* SchemaAwareResourceResolverBuilder builder | |||
* = ResourceResolverFactory.createSchemaAwareResourceResolverBuilder(defaultResolver); | |||
* builder.registerResourceResolverForSchema("test", testResolver); | |||
* builder.registerResourceResolverForSchema("anotherTest", test2Resolver); | |||
* ResourceResolver resolver = builder.build(); | |||
* </code></p> | |||
* This will result in all URIs for the form "test:///..." will be resolved using the | |||
* <code>testResolver</code> object; URIs of the form "anotherTest:///..." will be resolved | |||
* using <code>test2Resolver</code>; all other URIs will be resolved from the defaultResolver. | |||
*/ | |||
public interface SchemaAwareResourceResolverBuilder { | |||
/** | |||
* Register a schema with its respective {@link ResourceResolver}. This resolver will be | |||
* used as the only resolver for the specified schema. | |||
* | |||
* @param schema the schema to be used with the given resolver | |||
* @param resourceResolver the resource resolver | |||
*/ | |||
void registerResourceResolverForSchema(String schema, ResourceResolver resourceResolver); | |||
/** | |||
* Builds a {@link ResourceResolver} that will delegate to the respective resource resolver | |||
* when a registered URI schema is given | |||
* | |||
* @return a resolver that delegates to the appropriate schema resolver | |||
*/ | |||
ResourceResolver build(); | |||
} | |||
@@ -218,10 +265,12 @@ public final class ResourceResolverFactory { | |||
private static final SchemaAwareResourceResolverBuilder INSTANCE | |||
= new CompletedSchemaAwareResourceResolverBuilder(); | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
throw new IllegalStateException("Resource resolver already built"); | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
ResourceResolver resourceResolver) { | |||
throw new IllegalStateException("Resource resolver already built"); | |||
@@ -240,11 +289,13 @@ public final class ResourceResolverFactory { | |||
this.defaultResolver = defaultResolver; | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
ResourceResolver resourceResolver) { | |||
schemaHandlingResourceResolvers.put(schema, resourceResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
return new SchemaAwareResourceResolver( | |||
Collections.unmodifiableMap(schemaHandlingResourceResolvers), defaultResolver); | |||
@@ -261,11 +312,13 @@ public final class ResourceResolverFactory { | |||
this.delegate = new ActiveSchemaAwareResourceResolverBuilder(defaultResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
ResourceResolver resourceResolver) { | |||
delegate.registerResourceResolverForSchema(schema, resourceResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
ResourceResolver resourceResolver = delegate.build(); | |||
delegate = CompletedSchemaAwareResourceResolverBuilder.INSTANCE; |
@@ -43,6 +43,7 @@ import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.FopConfParser; | |||
import org.apache.fop.apps.FopFactory; | |||
import org.apache.fop.apps.FopFactoryBuilder; | |||
import org.apache.fop.apps.FopFactoryConfig; | |||
import org.apache.fop.apps.MimeConstants; | |||
import org.apache.fop.pdf.PDFAMode; | |||
import org.apache.fop.pdf.PDFEncryptionManager; | |||
@@ -117,7 +118,7 @@ public class CommandLineOptions { | |||
/* rendering options (for the user agent) */ | |||
private Map renderingOptions = new java.util.HashMap(); | |||
/* target resolution (for the user agent) */ | |||
private int targetResolution = 0; | |||
private float targetResolution = FopFactoryConfig.DEFAULT_TARGET_RESOLUTION; | |||
private boolean strictValidation = true; | |||
/* control memory-conservation policy */ | |||
@@ -138,7 +139,7 @@ public class CommandLineOptions { | |||
private boolean flushCache = false; | |||
private URI baseURI = new File(".").toURI(); | |||
private URI baseURI = new File(".").getAbsoluteFile().toURI(); | |||
/** | |||
* Construct a command line option object. | |||
@@ -420,7 +421,7 @@ public class CommandLineOptions { | |||
throw new FOPException("if you use '-cache', you must specify " | |||
+ "the name of the font cache file"); | |||
} else { | |||
factory.getFontManager().setCacheFile(new File(args[i + 1])); | |||
factory.getFontManager().setCacheFile(URI.create(args[i + 1])); | |||
return 1; | |||
} | |||
} | |||
@@ -468,7 +469,7 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
fofile = new File(filename); | |||
baseURI = fofile.toURI(); | |||
baseURI = getBaseURI(fofile); | |||
} | |||
return 1; | |||
} | |||
@@ -498,12 +499,16 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
xmlfile = new File(filename); | |||
baseURI = xmlfile.toURI(); | |||
baseURI = getBaseURI(xmlfile); | |||
} | |||
return 1; | |||
} | |||
} | |||
private URI getBaseURI(File file) { | |||
return file.getAbsoluteFile().getParentFile().toURI(); | |||
} | |||
private int parseAWTOutputOption(String[] args, int i) throws FOPException { | |||
setOutputMode(MimeConstants.MIME_FOP_AWT_PREVIEW); | |||
return 0; | |||
@@ -730,7 +735,7 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
fofile = new File(filename); | |||
baseURI = fofile.toURI(); | |||
baseURI = getBaseURI(fofile); | |||
} | |||
} else if (outputmode == null) { | |||
outputmode = MimeConstants.MIME_PDF; | |||
@@ -789,7 +794,7 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
areatreefile = new File(filename); | |||
baseURI = areatreefile.toURI(); | |||
baseURI = getBaseURI(areatreefile); | |||
} | |||
return 1; | |||
} | |||
@@ -806,7 +811,7 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
iffile = new File(filename); | |||
baseURI = iffile.toURI(); | |||
baseURI = getBaseURI(iffile); | |||
} | |||
return 1; | |||
} | |||
@@ -823,7 +828,7 @@ public class CommandLineOptions { | |||
this.useStdIn = true; | |||
} else { | |||
imagefile = new File(filename); | |||
baseURI = imagefile.toURI(); | |||
baseURI = getBaseURI(imagefile); | |||
} | |||
return 1; | |||
} |
@@ -19,6 +19,7 @@ | |||
package org.apache.fop.events; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
@@ -26,7 +27,7 @@ import java.util.List; | |||
*/ | |||
public class CompositeEventListener implements EventListener { | |||
private List listeners = new java.util.ArrayList(); | |||
private List<EventListener> listeners = new ArrayList<EventListener>(); | |||
/** | |||
* Adds an event listener to the broadcaster. It is appended to the list of previously | |||
@@ -46,22 +47,17 @@ public class CompositeEventListener implements EventListener { | |||
this.listeners.remove(listener); | |||
} | |||
private synchronized int getListenerCount() { | |||
return this.listeners.size(); | |||
} | |||
/** | |||
* Indicates whether any listeners have been registered with the broadcaster. | |||
* @return true if listeners are present, false otherwise | |||
*/ | |||
public boolean hasEventListeners() { | |||
return (getListenerCount() > 0); | |||
public synchronized boolean hasEventListeners() { | |||
return !listeners.isEmpty(); | |||
} | |||
/** {@inheritDoc} */ | |||
/** {@inheritDoc } */ | |||
public synchronized void processEvent(Event event) { | |||
for (int i = 0, c = getListenerCount(); i < c; i++) { | |||
EventListener listener = (EventListener)this.listeners.get(i); | |||
for (EventListener listener : listeners) { | |||
listener.processEvent(event); | |||
} | |||
} |
@@ -142,6 +142,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { | |||
delegate.endPageNumberCitationLast(pageLast); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
delegate.startStatic(staticContent); | |||
} | |||
@Override | |||
public void endStatic(StaticContent statisContent) { | |||
delegate.endStatic(statisContent); | |||
} | |||
@Override | |||
public void startFlow(Flow fl) { | |||
delegate.startFlow(fl); | |||
@@ -292,16 +302,6 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { | |||
delegate.endListBody(listItemBody); | |||
} | |||
@Override | |||
public void startStatic(StaticContent staticContent) { | |||
delegate.startStatic(staticContent); | |||
} | |||
@Override | |||
public void endStatic(StaticContent statisContent) { | |||
delegate.endStatic(statisContent); | |||
} | |||
@Override | |||
public void startMarkup() { | |||
delegate.startMarkup(); |
@@ -192,6 +192,20 @@ public abstract class FOEventHandler { | |||
public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { | |||
} | |||
/** | |||
* Process start of a Static. | |||
* @param staticContent StaticContent that is starting | |||
*/ | |||
public void startStatic(StaticContent staticContent) { | |||
} | |||
/** | |||
* Process end of a Static. | |||
* @param staticContent StaticContent that is ending | |||
*/ | |||
public void endStatic(StaticContent staticContent) { | |||
} | |||
/** | |||
* This method is called to indicate the start of a new fo:flow | |||
* or fo:static-content. | |||
@@ -409,22 +423,6 @@ public abstract class FOEventHandler { | |||
public void endListBody(ListItemBody listItemBody) { | |||
} | |||
// Static Regions | |||
/** | |||
* Process start of a Static. | |||
* @param staticContent StaticContent that is starting | |||
*/ | |||
public void startStatic(StaticContent staticContent) { | |||
} | |||
/** | |||
* Process end of a Static. | |||
* @param staticContent StaticContent that is ending | |||
*/ | |||
public void endStatic(StaticContent staticContent) { | |||
} | |||
/** | |||
* Process start of a Markup. | |||
*/ |
@@ -38,6 +38,7 @@ import org.apache.fop.accessibility.StructureTreeElement; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.complexscripts.bidi.DelimitedTextRange; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | |||
import org.apache.fop.fo.extensions.InternalElementMapping; | |||
@@ -601,6 +602,22 @@ public abstract class FONode implements Cloneable { | |||
getFOValidationEventProducer().missingProperty(this, getName(), propertyName, locator); | |||
} | |||
/** | |||
* Helper function to throw an error caused by an invalid property | |||
* | |||
* @param propertyName the name of the property. | |||
* @param propertyValue the value of the property. | |||
* * @param e optional property parsing exception. | |||
* @throws ValidationException the validation error provoked by the method call | |||
*/ | |||
protected void invalidPropertyValueError(String propertyName, String propertyValue, Exception e) | |||
throws ValidationException { | |||
getFOValidationEventProducer().invalidPropertyValue(this, getName(), propertyName, | |||
propertyValue, new PropertyException(e), locator); | |||
} | |||
/** | |||
* Helper function to return "Error(line#/column#)" string for | |||
* above exception messages |
@@ -26,6 +26,7 @@ import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.datatypes.LengthBase; | |||
import org.apache.fop.fo.expr.PropertyException; | |||
import org.apache.fop.fo.flow.table.TableFObj.ColumnNumberPropertyMaker; | |||
import org.apache.fop.fo.pagination.Flow; | |||
import org.apache.fop.fo.properties.BackgroundPositionShorthand; | |||
import org.apache.fop.fo.properties.BorderSpacingShorthandParser; | |||
import org.apache.fop.fo.properties.BorderWidthPropertyMaker; | |||
@@ -2267,7 +2268,7 @@ public final class FOPropertyMapping implements Constants { | |||
m = new StringProperty.Maker(PR_FLOW_NAME); | |||
m.setInherited(false); | |||
m.setDefault(""); | |||
addPropertyMaker("flow-name", m); | |||
addPropertyMaker(Flow.FLOW_NAME, m); | |||
// force-page-count | |||
m = new EnumProperty.Maker(PR_FORCE_PAGE_COUNT); |
@@ -36,6 +36,9 @@ import org.apache.fop.fo.properties.CommonAccessibilityHolder; | |||
*/ | |||
public class Flow extends FObj implements CommonAccessibilityHolder { | |||
/** The "flow-name" property name. */ | |||
public static final String FLOW_NAME = "flow-name"; | |||
private String flowName; | |||
private CommonAccessibility commonAccessibility; | |||
@@ -61,7 +64,7 @@ public class Flow extends FObj implements CommonAccessibilityHolder { | |||
/** {@inheritDoc} */ | |||
protected void startOfNode() throws FOPException { | |||
if (flowName == null || flowName.equals("")) { | |||
missingPropertyError("flow-name"); | |||
missingPropertyError(FLOW_NAME); | |||
} | |||
// according to communication from Paul Grosso (XSL-List, |
@@ -217,5 +217,24 @@ public class LayoutMasterSet extends FObj { | |||
public int getNameId() { | |||
return FO_LAYOUT_MASTER_SET; | |||
} | |||
/** | |||
* Returns the default name of the region to which the flow or static-content having | |||
* the given flow-name is assigned. | |||
* | |||
* @param flowName the value of the flow-name property | |||
* @return the default region name ("xsl-region-body", "xsl-region-before", etc.) | |||
*/ | |||
public String getDefaultRegionNameFor(String flowName) { | |||
for (SimplePageMaster spm : simplePageMasters.values()) { | |||
for (Region region : spm.getRegions().values()) { | |||
if (region.getRegionName().equals(flowName)) { | |||
return region.getDefaultRegionName(); | |||
} | |||
} | |||
} | |||
assert flowName.equals("xsl-before-float-separator") || flowName.equals("xsl-footnote-separator"); | |||
return flowName; | |||
} | |||
} | |||
@@ -42,7 +42,7 @@ public class StaticContent extends Flow { | |||
/** {@inheritDoc} */ | |||
protected void startOfNode() throws FOPException { | |||
if (getFlowName() == null || getFlowName().equals("")) { | |||
missingPropertyError("flow-name"); | |||
missingPropertyError(FLOW_NAME); | |||
} | |||
getFOEventHandler().startStatic(this); | |||
} |
@@ -29,7 +29,7 @@ import org.apache.fop.apps.io.InternalResourceResolver; | |||
public abstract class CIDFont extends CustomFont { | |||
/** Contains the character widths for all characters in the font */ | |||
protected int[] width = null; | |||
protected int[] width; | |||
/** | |||
* @param resourceResolver the URI resolver for controlling file access | |||
@@ -69,7 +69,7 @@ public abstract class CIDFont extends CustomFont { | |||
* Returns the subset information for this font. | |||
* @return the subset information | |||
*/ | |||
public abstract CIDSubset getCIDSubset(); | |||
public abstract CIDSet getCIDSet(); | |||
// ---- Optional ---- | |||
/** |
@@ -0,0 +1,113 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.fonts; | |||
import java.util.BitSet; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.fop.util.CharUtilities; | |||
/** | |||
* Provides methods to get font information. | |||
* Naming: | |||
* glyph index: original index of the glyph in the non-subset font (!= unicode index) | |||
* character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset | |||
* fonts, this is the same as the glyph index. | |||
* Unicode index: The Unicode codepoint of a character. | |||
* Glyph name: the Adobe glyph name (as found in Glyphs.java) | |||
*/ | |||
public class CIDFull implements CIDSet { | |||
private BitSet glyphIndices; | |||
private final MultiByteFont font; | |||
public CIDFull(MultiByteFont mbf) { | |||
font = mbf; | |||
} | |||
private void initGlyphIndices() { | |||
// this cannot be called in the constructor since the font is not ready... | |||
if (glyphIndices == null) { | |||
glyphIndices = font.getGlyphIndices(); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public int getOriginalGlyphIndex(int index) { | |||
return index; | |||
} | |||
/** {@inheritDoc} */ | |||
public char getUnicode(int index) { | |||
initGlyphIndices(); | |||
if (glyphIndices.get(index)) { | |||
return (char) index; | |||
} else { | |||
return CharUtilities.NOT_A_CHARACTER; | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public int mapChar(int glyphIndex, char unicode) { | |||
return (char) glyphIndex; | |||
} | |||
/** {@inheritDoc} */ | |||
public Map<Integer, Integer> getGlyphs() { | |||
// this is never really called for full embedded fonts but the equivalent map would be the identity | |||
initGlyphIndices(); | |||
Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>(); | |||
int nextBitSet = 0; | |||
for (int j = 0; j < glyphIndices.cardinality(); j++) { | |||
nextBitSet = glyphIndices.nextSetBit(nextBitSet); | |||
glyphs.put(Integer.valueOf(nextBitSet), Integer.valueOf(nextBitSet)); | |||
nextBitSet++; | |||
} | |||
return Collections.unmodifiableMap(glyphs); | |||
} | |||
/** {@inheritDoc} */ | |||
public char[] getChars() { | |||
return font.getChars(); | |||
} | |||
/** {@inheritDoc} */ | |||
public int getNumberOfGlyphs() { | |||
initGlyphIndices(); | |||
// note: the real number of glyphs is given by the cardinality() method (not the length()) but since | |||
// we will pad gaps in the indices with zeros we really want the length() here. this method is only | |||
// called when embedding a font in PostScript and this will be the value of the CIDCount entry | |||
return glyphIndices.length(); | |||
} | |||
/** {@inheritDoc} */ | |||
public BitSet getGlyphIndices() { | |||
initGlyphIndices(); | |||
return glyphIndices; | |||
} | |||
/** {@inheritDoc} */ | |||
public int[] getWidths() { | |||
return font.getWidths(); | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.fonts; | |||
import java.util.BitSet; | |||
import java.util.Map; | |||
/** | |||
* Declares methods to retrieve font information (glyph indices, widths, unicode values) from a CID font. | |||
*/ | |||
public interface CIDSet { | |||
/** | |||
* Returns the original index of the glyph inside the (non-subset) font's glyph list. This | |||
* index can be used to access the character width information, for example. | |||
* @param index the subset index (character selector) to access the glyph | |||
* @return the original index (or -1 if no glyph index is available for the subset index) | |||
*/ | |||
int getOriginalGlyphIndex(int index); | |||
/** | |||
* Returns the Unicode value for a subset index (character selector). If there's no such | |||
* Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. | |||
* @param subsetIndex the subset index (character selector) | |||
* @return the Unicode value or "NOT A CHARACTER" (0xFFFF) | |||
*/ | |||
char getUnicode(int index); | |||
/** | |||
* Maps a character to a character selector for a font subset. If the character isn't in the | |||
* subset, yet, it is added and a new character selector returned. Otherwise, the already | |||
* allocated character selector is returned from the existing map/subset. | |||
* @param glyphIndex the glyph index of the character | |||
* @param unicode the Unicode index of the character | |||
* @return the subset index | |||
*/ | |||
int mapChar(int glyphIndex, char unicode); | |||
/** | |||
* Returns an unmodifiable Map of the font subset. It maps from glyph index to | |||
* character selector (i.e. the subset index in this case). | |||
* @return Map Map<Integer, Integer> of the font subset | |||
*/ | |||
Map<Integer, Integer> getGlyphs(); | |||
/** | |||
* Returns a char array containing all Unicode characters that are in the subset. | |||
* @return a char array with all used Unicode characters | |||
*/ | |||
char[] getChars(); | |||
/** | |||
* Returns the number of glyphs in the subset. | |||
* @return the number of glyphs in the subset | |||
*/ | |||
int getNumberOfGlyphs(); | |||
/** | |||
* Returns a BitSet with bits set for each available glyph index in the subset. | |||
* @return a BitSet indicating available glyph indices | |||
*/ | |||
BitSet getGlyphIndices(); | |||
/** | |||
* Return the array of widths. | |||
* <p> | |||
* This is used to get an array for inserting in an output format. | |||
* It should not be used for lookup. | |||
* @return an array of widths | |||
*/ | |||
int[] getWidths(); | |||
} |
@@ -26,18 +26,16 @@ import java.util.Map; | |||
import org.apache.fop.util.CharUtilities; | |||
//Naming: | |||
//glyph index: original index of the glyph in the non-subset font (!= unicode index) | |||
//character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For | |||
// non-subset fonts, this is the same as the glyph index. | |||
//Unicode index: The Unicode codepoint of a character. | |||
//Glyph name: the Adobe glyph name (as found in Glyphs.java) | |||
/** | |||
* Keeps track of the glyphs used in a document. This information is later used to build | |||
* a subset of a font. | |||
* Provides methods to get font information. | |||
* Naming: | |||
* glyph index: original index of the glyph in the non-subset font (!= unicode index) | |||
* character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset | |||
* fonts, this is the same as the glyph index. | |||
* Unicode index: The Unicode codepoint of a character. | |||
* Glyph name: the Adobe glyph name (as found in Glyphs.java) | |||
*/ | |||
public class CIDSubset { | |||
public class CIDSubset implements CIDSet { | |||
/** | |||
* usedGlyphs contains orginal, new glyph index (glyph index -> char selector) | |||
@@ -48,51 +46,36 @@ public class CIDSubset { | |||
* usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) | |||
*/ | |||
private Map<Integer, Integer> usedGlyphsIndex = new HashMap<Integer, Integer>(); | |||
private int usedGlyphsCount = 0; | |||
private int usedGlyphsCount; | |||
/** | |||
* usedCharsIndex contains new glyph, original char (char selector -> Unicode) | |||
*/ | |||
private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>(); | |||
/** | |||
* Default constructor. | |||
*/ | |||
public CIDSubset() { | |||
} | |||
private final MultiByteFont font; | |||
/** | |||
* Adds the first glyph which is reserved for .notdef for all CID subsets. | |||
*/ | |||
public void setupFirstGlyph() { | |||
usedGlyphs.put(Integer.valueOf(0), Integer.valueOf(0)); | |||
usedGlyphsIndex.put(Integer.valueOf(0), Integer.valueOf(0)); | |||
public CIDSubset(MultiByteFont mbf) { | |||
font = mbf; | |||
// The zeroth value is reserved for .notdef | |||
usedGlyphs.put(0, 0); | |||
usedGlyphsIndex.put(0, 0); | |||
usedGlyphsCount++; | |||
} | |||
/** | |||
* Returns the original index of the glyph inside the (non-subset) font's glyph list. This | |||
* index can be used to access the character width information, for example. | |||
* @param subsetIndex the subset index (character selector) to access the glyph | |||
* @return the original index (or -1 if no glyph index is available for the subset index) | |||
*/ | |||
public int getGlyphIndexForSubsetIndex(int subsetIndex) { | |||
Integer glyphIndex = usedGlyphsIndex.get(Integer.valueOf(subsetIndex)); | |||
/** {@inheritDoc} */ | |||
public int getOriginalGlyphIndex(int index) { | |||
Integer glyphIndex = usedGlyphsIndex.get(index); | |||
if (glyphIndex != null) { | |||
return glyphIndex.intValue(); | |||
return glyphIndex; | |||
} else { | |||
return -1; | |||
} | |||
} | |||
/** | |||
* Returns the Unicode value for a subset index (character selector). If there's no such | |||
* Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. | |||
* @param subsetIndex the subset index (character selector) | |||
* @return the Unicode value or "NOT A CHARACTER" (0xFFFF) | |||
*/ | |||
public char getUnicodeForSubsetIndex(int subsetIndex) { | |||
Character mapValue = usedCharsIndex.get(Integer.valueOf(subsetIndex)); | |||
/** {@inheritDoc} */ | |||
public char getUnicode(int index) { | |||
Character mapValue = usedCharsIndex.get(index); | |||
if (mapValue != null) { | |||
return mapValue.charValue(); | |||
} else { | |||
@@ -100,72 +83,60 @@ public class CIDSubset { | |||
} | |||
} | |||
/** | |||
* Maps a character to a character selector for a font subset. If the character isn't in the | |||
* subset, yet, it is added and a new character selector returned. Otherwise, the already | |||
* allocated character selector is returned from the existing map/subset. | |||
* @param glyphIndex the glyph index of the character | |||
* @param unicode the Unicode index of the character | |||
* @return the subset index | |||
*/ | |||
public int mapSubsetChar(int glyphIndex, char unicode) { | |||
/** {@inheritDoc} */ | |||
public int mapChar(int glyphIndex, char unicode) { | |||
// Reencode to a new subset font or get the reencoded value | |||
// IOW, accumulate the accessed characters and build a character map for them | |||
Integer subsetCharSelector = usedGlyphs.get(Integer.valueOf(glyphIndex)); | |||
Integer subsetCharSelector = usedGlyphs.get(glyphIndex); | |||
if (subsetCharSelector == null) { | |||
int selector = usedGlyphsCount; | |||
usedGlyphs.put(Integer.valueOf(glyphIndex), | |||
Integer.valueOf(selector)); | |||
usedGlyphsIndex.put(Integer.valueOf(selector), | |||
Integer.valueOf(glyphIndex)); | |||
usedCharsIndex.put(Integer.valueOf(selector), | |||
Character.valueOf(unicode)); | |||
usedGlyphs.put(glyphIndex, selector); | |||
usedGlyphsIndex.put(selector, glyphIndex); | |||
usedCharsIndex.put(selector, unicode); | |||
usedGlyphsCount++; | |||
return selector; | |||
} else { | |||
return subsetCharSelector.intValue(); | |||
return subsetCharSelector; | |||
} | |||
} | |||
/** | |||
* Returns an unmodifiable Map of the font subset. It maps from glyph index to | |||
* character selector (i.e. the subset index in this case). | |||
* @return Map Map<Integer, Integer> of the font subset | |||
*/ | |||
public Map<Integer, Integer> getSubsetGlyphs() { | |||
/** {@inheritDoc} */ | |||
public Map<Integer, Integer> getGlyphs() { | |||
return Collections.unmodifiableMap(this.usedGlyphs); | |||
} | |||
/** | |||
* Returns a char array containing all Unicode characters that are in the subset. | |||
* @return a char array with all used Unicode characters | |||
*/ | |||
public char[] getSubsetChars() { | |||
/** {@inheritDoc} */ | |||
public char[] getChars() { | |||
char[] charArray = new char[usedGlyphsCount]; | |||
for (int i = 0; i < usedGlyphsCount; i++) { | |||
charArray[i] = getUnicodeForSubsetIndex(i); | |||
charArray[i] = getUnicode(i); | |||
} | |||
return charArray; | |||
} | |||
/** | |||
* Returns the number of glyphs in the subset. | |||
* @return the number of glyphs in the subset | |||
*/ | |||
public int getSubsetSize() { | |||
/** {@inheritDoc} */ | |||
public int getNumberOfGlyphs() { | |||
return this.usedGlyphsCount; | |||
} | |||
/** | |||
* Returns a BitSet with bits set for each available glyph index in the subset. | |||
* @return a BitSet indicating available glyph indices | |||
*/ | |||
public BitSet getGlyphIndexBitSet() { | |||
/** {@inheritDoc} */ | |||
public BitSet getGlyphIndices() { | |||
BitSet bitset = new BitSet(); | |||
for (Integer cid : usedGlyphs.keySet()) { | |||
bitset.set(cid.intValue()); | |||
bitset.set(cid); | |||
} | |||
return bitset; | |||
} | |||
/** {@inheritDoc} */ | |||
public int[] getWidths() { | |||
int[] widths = font.getWidths(); | |||
int[] tmpWidth = new int[getNumberOfGlyphs()]; | |||
for (int i = 0, c = getNumberOfGlyphs(); i < c; i++) { | |||
int nwx = Math.max(0, getOriginalGlyphIndex(i)); | |||
tmpWidth[i] = widths[nwx]; | |||
} | |||
return tmpWidth; | |||
} | |||
} |
@@ -89,7 +89,7 @@ public final class DefaultFontConfig implements FontConfig { | |||
} else { | |||
this.strict = strict; | |||
this.fontInfoCfg = cfg.getChild("fonts", false); | |||
instance = new DefaultFontConfig(cfg.getChild("auto-detect", false) != null); | |||
instance = new DefaultFontConfig(fontInfoCfg.getChild("auto-detect", false) != null); | |||
parse(); | |||
} | |||
} |
@@ -53,7 +53,7 @@ public final class FontCache implements Serializable { | |||
* Serialization Version UID. Change this value if you want to make sure the | |||
* user's cache file is purged after an update. | |||
*/ | |||
private static final long serialVersionUID = 605232520271754719L; | |||
private static final long serialVersionUID = 9129238336422194339L; | |||
/** logging instance */ | |||
private static Log log = LogFactory.getLog(FontCache.class); | |||
@@ -65,7 +65,7 @@ public final class FontCache implements Serializable { | |||
private static final String DEFAULT_CACHE_FILENAME = "fop-fonts.cache"; | |||
/** has this cache been changed since it was last read? */ | |||
private transient boolean changed = false; | |||
private transient boolean changed; | |||
/** change lock */ | |||
private final boolean[] changeLock = new boolean[1]; | |||
@@ -73,12 +73,14 @@ public final class FontCache implements Serializable { | |||
/** | |||
* master mapping of font url -> font info. This needs to be a list, since a | |||
* TTC file may contain more than 1 font. | |||
* @serial | |||
*/ | |||
private Map<String, CachedFontFile> fontfileMap = null; | |||
/** | |||
* mapping of font url -> file modified date (for all fonts that have failed | |||
* to load) | |||
* @serial | |||
*/ | |||
private Map<String, Long> failedFontMap = null; | |||
@@ -139,6 +141,7 @@ public final class FontCache implements Serializable { | |||
* | |||
* @return the font cache deserialized from the file (or null if no cache | |||
* file exists or if it could not be read) | |||
* @deprecated use {@link #loadFrom(File)} instead | |||
*/ | |||
public static FontCache load() { | |||
return loadFrom(getDefaultCacheFile(false)); | |||
@@ -190,8 +193,8 @@ public final class FontCache implements Serializable { | |||
/** | |||
* Writes the font cache to disk. | |||
* | |||
* @throws FOPException | |||
* fop exception | |||
* @throws FOPException fop exception | |||
* @deprecated use {@link #saveTo(File)} instead | |||
*/ | |||
public void save() throws FOPException { | |||
saveTo(getDefaultCacheFile(true)); |
@@ -19,7 +19,7 @@ | |||
package org.apache.fop.fonts; | |||
import java.io.File; | |||
import java.net.URI; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -29,25 +29,27 @@ import org.apache.fop.apps.FOPException; | |||
*/ | |||
public interface FontCacheManager { | |||
/** | |||
* Sets the font cache file given the URI pointing to the file. | |||
* @param fontCacheURI the font cache URI | |||
*/ | |||
void setCacheFile(URI fontCacheURI); | |||
/** | |||
* Loads the font cache into memory from the given file. | |||
* @param file the serialized font cache | |||
* @return the de-serialized font cache | |||
*/ | |||
FontCache load(File file); | |||
FontCache load(); | |||
/** | |||
* Serializes the font cache to file. | |||
* @param file the file to serialize the font cache to | |||
* @throws FOPException if an error occurs serializing the font cache | |||
*/ | |||
void save(File file) throws FOPException; | |||
void save() throws FOPException; | |||
/** | |||
* Deletes the font cache from the file-system. | |||
* @param file delete the serialized font cache | |||
* @throws FOPException if an error occurs deleting the font cache | |||
*/ | |||
void delete(File file) throws FOPException; | |||
void delete() throws FOPException; | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.apache.fop.fonts; | |||
import java.io.File; | |||
import java.net.URI; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -50,11 +51,14 @@ public final class FontCacheManagerFactory { | |||
private static final class FontCacheManagerImpl implements FontCacheManager { | |||
/** Provides a font cache file path **/ | |||
private File cacheFile; | |||
private FontCache fontCache; | |||
public FontCache load(File cacheFile) { | |||
public FontCache load() { | |||
if (fontCache == null) { | |||
fontCache = FontCache.loadFrom(cacheFile); | |||
fontCache = FontCache.loadFrom(getCacheFile(false)); | |||
if (fontCache == null) { | |||
fontCache = new FontCache(); | |||
} | |||
@@ -62,31 +66,46 @@ public final class FontCacheManagerFactory { | |||
return fontCache; | |||
} | |||
public void save(File cacheFile) throws FOPException { | |||
public void save() throws FOPException { | |||
if (fontCache != null && fontCache.hasChanged()) { | |||
fontCache.saveTo(cacheFile); | |||
fontCache.saveTo(getCacheFile(true)); | |||
} | |||
} | |||
public void delete(File cacheFile) throws FOPException { | |||
if (!cacheFile.delete()) { | |||
public void delete() throws FOPException { | |||
if (!getCacheFile(true).delete()) { | |||
throw new FOPException("Failed to flush the font cache file '" + cacheFile + "'."); | |||
} | |||
} | |||
private File getCacheFile(boolean forWriting) { | |||
if (cacheFile != null) { | |||
return cacheFile; | |||
} | |||
return FontCache.getDefaultCacheFile(forWriting); | |||
} | |||
public void setCacheFile(URI fontCacheURI) { | |||
cacheFile = new File(fontCacheURI); | |||
} | |||
} | |||
private static final class DisabledFontCacheManager implements FontCacheManager { | |||
public FontCache load(File cacheFile) { | |||
public FontCache load() { | |||
return null; | |||
} | |||
public void save(File cacheFile) throws FOPException { | |||
public void save() throws FOPException { | |||
// nop | |||
} | |||
public void delete(File cacheFile) throws FOPException { | |||
public void delete() throws FOPException { | |||
throw new FOPException("Font Cache disabled"); | |||
} | |||
public void setCacheFile(URI fontCacheURI) { | |||
// nop | |||
} | |||
} | |||
} |
@@ -19,7 +19,7 @@ | |||
package org.apache.fop.fonts; | |||
import java.io.File; | |||
import java.net.URI; | |||
import java.util.List; | |||
import org.apache.fop.apps.FOPException; | |||
@@ -52,9 +52,6 @@ public class FontManager { | |||
/** FontTriplet matcher for fonts that shall be referenced rather than embedded. */ | |||
private FontTriplet.Matcher referencedFontsMatcher; | |||
/** Provides a font cache file path **/ | |||
private File cacheFile; | |||
/** | |||
* Main constructor | |||
* | |||
@@ -115,25 +112,10 @@ public class FontManager { | |||
/** | |||
* Sets the font cache file | |||
* @param cacheFile the font cache file | |||
*/ | |||
public void setCacheFile(File cacheFile) { | |||
this.cacheFile = cacheFile; | |||
} | |||
/** | |||
* Returns the font cache file | |||
* @return the font cache file | |||
* @param cacheFileURI the URI of the font cache file | |||
*/ | |||
public File getCacheFile() { | |||
return getCacheFile(false); | |||
} | |||
private File getCacheFile(boolean writable) { | |||
if (cacheFile != null) { | |||
return cacheFile; | |||
} | |||
return FontCache.getDefaultCacheFile(writable); | |||
public void setCacheFile(URI cacheFileURI) { | |||
fontCacheManager.setCacheFile(resourceResolver.resolveFromBase(cacheFileURI)); | |||
} | |||
/** | |||
@@ -148,7 +130,7 @@ public class FontManager { | |||
* @return the font cache | |||
*/ | |||
public FontCache getFontCache() { | |||
return fontCacheManager.load(getCacheFile()); | |||
return fontCacheManager.load(); | |||
} | |||
/** | |||
@@ -157,7 +139,7 @@ public class FontManager { | |||
* @throws FOPException fop exception | |||
*/ | |||
public void saveCache() throws FOPException { | |||
fontCacheManager.save(getCacheFile()); | |||
fontCacheManager.save(); | |||
} | |||
/** | |||
@@ -165,7 +147,7 @@ public class FontManager { | |||
* @throws FOPException if an error was thrown while deleting the cache | |||
*/ | |||
public void deleteCache() throws FOPException { | |||
fontCacheManager.delete(getCacheFile(true)); | |||
fontCacheManager.delete(); | |||
} | |||
/** |
@@ -19,7 +19,6 @@ | |||
package org.apache.fop.fonts; | |||
import java.io.File; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import java.util.List; | |||
@@ -72,6 +71,19 @@ public class FontManagerConfigurator { | |||
* @throws FOPException if an exception occurs while processing the configuration | |||
*/ | |||
public void configure(FontManager fontManager, boolean strict) throws FOPException { | |||
if (cfg.getChild("font-base", false) != null) { | |||
try { | |||
URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("font-base") | |||
.getValue(null)); | |||
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( | |||
defaultBaseUri.resolve(fontBase), resourceResolver)); | |||
} catch (URISyntaxException use) { | |||
LogUtil.handleException(log, use, true); | |||
} | |||
} else { | |||
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( | |||
defaultBaseUri, resourceResolver)); | |||
} | |||
// caching (fonts) | |||
if (cfg.getChild("use-cache", false) != null) { | |||
try { | |||
@@ -79,27 +91,14 @@ public class FontManagerConfigurator { | |||
fontManager.disableFontCache(); | |||
} else { | |||
if (cfg.getChild("cache-file", false) != null) { | |||
fontManager.setCacheFile(new File(cfg.getChild("cache-file").getValue())); | |||
fontManager.setCacheFile(URI.create(cfg.getChild("cache-file").getValue())); | |||
} | |||
} | |||
} catch (ConfigurationException mfue) { | |||
LogUtil.handleException(log, mfue, true); | |||
} | |||
} | |||
if (cfg.getChild("font-base", false) != null) { | |||
try { | |||
URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("font-base").getValue( | |||
null)); | |||
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( | |||
defaultBaseUri.resolve(fontBase), resourceResolver)); | |||
} catch (URISyntaxException use) { | |||
LogUtil.handleException(log, use, true); | |||
} | |||
} else { | |||
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( | |||
defaultBaseUri, resourceResolver)); | |||
} | |||
// [GA] permit configuration control over base14 kerning; without this, | |||
// there is no way for a user to enable base14 kerning other than by | |||
// programmatic API; |
@@ -157,7 +157,7 @@ public class FontReader extends DefaultHandler { | |||
throws SAXException { | |||
if (localName.equals("font-metrics")) { | |||
if ("TYPE0".equals(attributes.getValue("type"))) { | |||
multiFont = new MultiByteFont(resourceResolver); | |||
multiFont = new MultiByteFont(resourceResolver, EmbeddingMode.AUTO); | |||
returnFont = multiFont; | |||
isCID = true; | |||
TTFReader.checkMetricsVersion(attributes); |
@@ -438,6 +438,9 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, | |||
*/ | |||
public boolean isSubsetEmbedded() { | |||
load(true); | |||
if (realFont.isMultiByte() && this.embeddingMode == EmbeddingMode.FULL) { | |||
return false; | |||
} | |||
return realFont.isMultiByte(); | |||
} | |||
@@ -21,6 +21,7 @@ package org.apache.fop.fonts; | |||
import java.nio.CharBuffer; | |||
import java.nio.IntBuffer; | |||
import java.util.BitSet; | |||
import java.util.Map; | |||
import org.apache.commons.logging.Log; | |||
@@ -44,13 +45,13 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
private static final Log log // CSOK: ConstantNameCheck | |||
= LogFactory.getLog(MultiByteFont.class); | |||
private String ttcName = null; | |||
private String ttcName; | |||
private String encoding = "Identity-H"; | |||
private int defaultWidth = 0; | |||
private int defaultWidth; | |||
private CIDFontType cidType = CIDFontType.CIDTYPE2; | |||
private CIDSubset subset = new CIDSubset(); | |||
private final CIDSet cidSet; | |||
/* advanced typographic support */ | |||
private GlyphDefinitionTable gdef; | |||
@@ -69,10 +70,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
/** | |||
* Default constructor | |||
*/ | |||
public MultiByteFont(InternalResourceResolver resourceResolver) { | |||
public MultiByteFont(InternalResourceResolver resourceResolver, EmbeddingMode embeddingMode) { | |||
super(resourceResolver); | |||
subset.setupFirstGlyph(); | |||
setFontType(FontType.TYPE0); | |||
setEmbeddingMode(embeddingMode); | |||
if (embeddingMode != EmbeddingMode.FULL) { | |||
cidSet = new CIDSubset(this); | |||
} else { | |||
cidSet = new CIDFull(this); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -129,13 +135,16 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
} | |||
public boolean isSubsetEmbedded() { | |||
if (getEmbeddingMode() == EmbeddingMode.FULL) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public CIDSubset getCIDSubset() { | |||
return this.subset; | |||
public CIDSet getCIDSet() { | |||
return this.cidSet; | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -147,7 +156,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
/** {@inheritDoc} */ | |||
public int getWidth(int i, int size) { | |||
if (isEmbeddable()) { | |||
int glyphIndex = subset.getGlyphIndexForSubsetIndex(i); | |||
int glyphIndex = cidSet.getOriginalGlyphIndex(i); | |||
return size * width[glyphIndex]; | |||
} else { | |||
return size * width[i]; | |||
@@ -283,9 +292,39 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); | |||
} | |||
if (isEmbeddable()) { | |||
glyphIndex = subset.mapSubsetChar(glyphIndex, c); | |||
glyphIndex = cidSet.mapChar(glyphIndex, c); | |||
} | |||
return (char)glyphIndex; | |||
return (char) glyphIndex; | |||
} | |||
protected BitSet getGlyphIndices() { | |||
BitSet bitset = new BitSet(); | |||
bitset.set(0); | |||
bitset.set(1); | |||
bitset.set(2); | |||
for (int i = 0; i < cmap.length; i++) { | |||
int start = cmap[i].getUnicodeStart(); | |||
int end = cmap[i].getUnicodeEnd(); | |||
int glyphIndex = cmap[i].getGlyphStartIndex(); | |||
while (start++ < end + 1) { | |||
bitset.set(glyphIndex++); | |||
} | |||
} | |||
return bitset; | |||
} | |||
protected char[] getChars() { | |||
// the width array is set when the font is built | |||
char[] chars = new char[width.length]; | |||
for (int i = 0; i < cmap.length; i++) { | |||
int start = cmap[i].getUnicodeStart(); | |||
int end = cmap[i].getUnicodeEnd(); | |||
int glyphIndex = cmap[i].getGlyphStartIndex(); | |||
while (start < end + 1) { | |||
chars[glyphIndex++] = (char) start++; | |||
} | |||
} | |||
return chars; | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -331,15 +370,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
* @return Map Map of used Glyphs | |||
*/ | |||
public Map<Integer, Integer> getUsedGlyphs() { | |||
return subset.getSubsetGlyphs(); | |||
} | |||
/** @return an array of the chars used */ | |||
public char[] getCharsUsed() { | |||
if (!isEmbeddable()) { | |||
return null; | |||
} | |||
return subset.getSubsetChars(); | |||
return cidSet.getGlyphs(); | |||
} | |||
/** |
@@ -127,7 +127,7 @@ public class TTFFontLoader extends FontLoader { | |||
} | |||
if (isCid) { | |||
multiFont = new MultiByteFont(resourceResolver); | |||
multiFont = new MultiByteFont(resourceResolver, embeddingMode); | |||
returnFont = multiFont; | |||
multiFont.setTTCName(ttcFontName); | |||
} else { |
@@ -21,6 +21,8 @@ package org.apache.fop.layoutmgr; | |||
import java.util.List; | |||
import org.apache.fop.fo.Constants; | |||
/** | |||
* This class represents an unresolved break possibility. | |||
*/ | |||
@@ -28,7 +30,7 @@ public class BreakElement extends UnresolvedListElement { | |||
private int penaltyWidth; | |||
private int penaltyValue; | |||
private int breakClass = -1; | |||
private int breakClass; | |||
private List pendingBeforeMarks; | |||
private List pendingAfterMarks; | |||
@@ -39,7 +41,7 @@ public class BreakElement extends UnresolvedListElement { | |||
* @param context the layout context which contains the pending conditional elements | |||
*/ | |||
public BreakElement(Position position, int penaltyValue, LayoutContext context) { | |||
this(position, penaltyValue, -1, context); | |||
this(position, penaltyValue, Constants.EN_AUTO, context); | |||
} | |||
/** | |||
@@ -80,7 +82,7 @@ public class BreakElement extends UnresolvedListElement { | |||
super(position); | |||
this.penaltyWidth = penaltyWidth; | |||
this.penaltyValue = penaltyValue; | |||
this.breakClass = breakClass; | |||
setBreakClass(breakClass); | |||
this.pendingBeforeMarks = context.getPendingBeforeMarks(); | |||
this.pendingAfterMarks = context.getPendingAfterMarks(); | |||
} | |||
@@ -142,13 +144,24 @@ public class BreakElement extends UnresolvedListElement { | |||
* | |||
* @param breakClass one of | |||
* {@link org.apache.fop.fo.Constants#EN_AUTO}, | |||
* {@link org.apache.fop.fo.Constants#EN_LINE}, | |||
* {@link org.apache.fop.fo.Constants#EN_COLUMN}, | |||
* {@link org.apache.fop.fo.Constants#EN_PAGE}, | |||
* {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE}, | |||
* {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}. | |||
*/ | |||
public void setBreakClass(int breakClass) { | |||
this.breakClass = breakClass; | |||
switch (breakClass) { | |||
case Constants.EN_AUTO: | |||
case Constants.EN_LINE: | |||
case Constants.EN_COLUMN: | |||
case Constants.EN_PAGE: | |||
case Constants.EN_EVEN_PAGE: | |||
case Constants.EN_ODD_PAGE: | |||
this.breakClass = breakClass; | |||
break; | |||
default: throw new IllegalArgumentException("Illegal value for break class: " + breakClass); | |||
} | |||
} | |||
/** @return the pending border and padding elements at the before edge */ |
@@ -95,8 +95,7 @@ public class PageBreaker extends AbstractBreaker { | |||
return new PageBreakingLayoutListener() { | |||
public void notifyOverflow(int part, int amount, FObj obj) { | |||
Page p = pageProvider.getPage( | |||
false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST); | |||
Page p = pageProvider.getPageFromColumnIndex(part); | |||
RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion( | |||
Region.FO_REGION_BODY); | |||
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( |
@@ -151,18 +151,37 @@ public class PageProvider implements Constants { | |||
return this.lastReportedBPD; | |||
} | |||
// Wish there were a more elegant way to do this in Java | |||
private int[] getColIndexAndColCount(int index) { | |||
private static class Column { | |||
final Page page; | |||
final int pageIndex; | |||
final int colIndex; | |||
final int columnCount; | |||
Column(Page page, int pageIndex, int colIndex, int columnCount) { | |||
this.page = page; | |||
this.pageIndex = pageIndex; | |||
this.colIndex = colIndex; | |||
this.columnCount = columnCount; | |||
} | |||
} | |||
private Column getColumn(int index) { | |||
int columnCount = 0; | |||
int colIndex = startColumnOfCurrentElementList + index; | |||
int pageIndex = -1; | |||
Page page; | |||
do { | |||
colIndex -= columnCount; | |||
pageIndex++; | |||
Page page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||
page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||
columnCount = page.getPageViewport().getCurrentSpan().getColumnCount(); | |||
} while (colIndex >= columnCount); | |||
return new int[] {colIndex, columnCount}; | |||
return new Column(page, pageIndex, colIndex, columnCount); | |||
} | |||
/** | |||
@@ -173,22 +192,13 @@ public class PageProvider implements Constants { | |||
* than, equal to or greater than the IPD of the following part | |||
*/ | |||
public int compareIPDs(int index) { | |||
int columnCount = 0; | |||
int colIndex = startColumnOfCurrentElementList + index; | |||
int pageIndex = -1; | |||
Page page; | |||
do { | |||
colIndex -= columnCount; | |||
pageIndex++; | |||
page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||
columnCount = page.getPageViewport().getCurrentSpan().getColumnCount(); | |||
} while (colIndex >= columnCount); | |||
if (colIndex + 1 < columnCount) { | |||
Column column = getColumn(index); | |||
if (column.colIndex + 1 < column.columnCount) { | |||
// Next part is a column on same page => same IPD | |||
return 0; | |||
} else { | |||
Page nextPage = getPage(false, pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST); | |||
return page.getPageViewport().getBodyRegion().getIPD() | |||
Page nextPage = getPage(false, column.pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST); | |||
return column.page.getPageViewport().getBodyRegion().getIPD() | |||
- nextPage.getPageViewport().getBodyRegion().getIPD(); | |||
} | |||
} | |||
@@ -199,7 +209,7 @@ public class PageProvider implements Constants { | |||
* @return {@code true} if the break starts a new page | |||
*/ | |||
boolean startPage(int index) { | |||
return getColIndexAndColCount(index)[0] == 0; | |||
return getColumn(index).colIndex == 0; | |||
} | |||
/** | |||
@@ -208,8 +218,8 @@ public class PageProvider implements Constants { | |||
* @return {@code true} if the break ends a page | |||
*/ | |||
boolean endPage(int index) { | |||
int[] colIndexAndColCount = getColIndexAndColCount(index); | |||
return colIndexAndColCount[0] == colIndexAndColCount[1] - 1; | |||
Column column = getColumn(index); | |||
return column.colIndex == column.columnCount - 1; | |||
} | |||
/** | |||
@@ -219,7 +229,7 @@ public class PageProvider implements Constants { | |||
* @return the number of columns | |||
*/ | |||
int getColumnCount(int index) { | |||
return getColIndexAndColCount(index)[1]; | |||
return getColumn(index).columnCount; | |||
} | |||
/** | |||
@@ -229,24 +239,12 @@ public class PageProvider implements Constants { | |||
* @return the requested part index | |||
*/ | |||
public int getStartingPartIndexForLastPage(int partCount) { | |||
int result = 0; | |||
int idx = 0; | |||
int pageIndex = 0; | |||
int colIndex = startColumnOfCurrentElementList; | |||
Page page = getPage( | |||
false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||
while (idx < partCount) { | |||
if ((colIndex >= page.getPageViewport().getCurrentSpan().getColumnCount())) { | |||
colIndex = 0; | |||
pageIndex++; | |||
page = getPage( | |||
false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); | |||
result = idx; | |||
} | |||
colIndex++; | |||
idx++; | |||
} | |||
return result; | |||
int lastPartIndex = partCount - 1; | |||
return lastPartIndex - getColumn(lastPartIndex).colIndex; | |||
} | |||
Page getPageFromColumnIndex(int columnIndex) { | |||
return getColumn(columnIndex).page; | |||
} | |||
/** | |||
@@ -291,7 +289,9 @@ public class PageProvider implements Constants { | |||
log.trace("last page requested: " + index); | |||
} | |||
} | |||
while (intIndex >= cachedPages.size()) { | |||
if (intIndex > cachedPages.size()) { | |||
throw new UnsupportedOperationException("Cannot handle holes in page cache"); | |||
} else if (intIndex == cachedPages.size()) { | |||
if (log.isTraceEnabled()) { | |||
log.trace("Caching " + index); | |||
} |
@@ -649,6 +649,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager | |||
log.trace("Restarting line breaking from index " + restartPosition.getIndex()); | |||
int parIndex = restartPosition.getLeafPos(); | |||
KnuthSequence paragraph = knuthParagraphs.get(parIndex); | |||
if (paragraph instanceof Paragraph) { | |||
((Paragraph) paragraph).ignoreAtStart = 0; | |||
isFirstInBlock = false; | |||
} | |||
paragraph.subList(0, restartPosition.getIndex() + 1).clear(); | |||
Iterator<KnuthElement> iter = paragraph.iterator(); | |||
while (iter.hasNext() && !iter.next().isBox()) { |
@@ -39,6 +39,7 @@ import org.apache.fop.fo.properties.KeepProperty; | |||
import org.apache.fop.layoutmgr.BlockLevelEventProducer; | |||
import org.apache.fop.layoutmgr.BlockStackingLayoutManager; | |||
import org.apache.fop.layoutmgr.BreakElement; | |||
import org.apache.fop.layoutmgr.BreakOpportunity; | |||
import org.apache.fop.layoutmgr.ConditionalElementListener; | |||
import org.apache.fop.layoutmgr.KnuthElement; | |||
import org.apache.fop.layoutmgr.KnuthGlue; | |||
@@ -62,7 +63,7 @@ import org.apache.fop.util.BreakUtil; | |||
* the render background. | |||
*/ | |||
public class TableLayoutManager extends BlockStackingLayoutManager | |||
implements ConditionalElementListener { | |||
implements ConditionalElementListener, BreakOpportunity { | |||
/** | |||
* logging instance |
@@ -37,6 +37,7 @@ import java.util.Map; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope; | |||
import org.apache.fop.pdf.xref.CrossReferenceStream; | |||
import org.apache.fop.pdf.xref.CrossReferenceTable; | |||
import org.apache.fop.pdf.xref.TrailerDictionary; | |||
@@ -354,19 +355,20 @@ public class PDFDocument { | |||
} | |||
/** | |||
* Creates and returns a structure element. | |||
* | |||
* @param structureType the structure type of the new element (value for the | |||
* S entry) | |||
* @param parent the parent of the new structure element in the structure | |||
* hierarchy | |||
* @return a dictionary of type StructElem | |||
* Adds the given element to the structure tree. | |||
*/ | |||
public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { | |||
PDFStructElem structElem = new PDFStructElem(parent, structureType); | |||
public void registerStructureElement(PDFStructElem structElem) { | |||
assignObjectNumber(structElem); | |||
structureTreeElements.add(structElem); | |||
return structElem; | |||
} | |||
/** | |||
* Assigns the given scope to the given element and adds it to the structure tree. The | |||
* scope may not be added if it's not compatible with this document's PDF version. | |||
*/ | |||
public void registerStructureElement(PDFStructElem structElem, Scope scope) { | |||
registerStructureElement(structElem); | |||
versionController.addTableHeaderScopeAttribute(structElem, scope); | |||
} | |||
/** |
@@ -23,6 +23,9 @@ package org.apache.fop.pdf; | |||
import java.util.Collections; | |||
import java.util.Set; | |||
import org.apache.fop.fonts.CodePointMapping; | |||
import org.apache.fop.fonts.SingleByteEncoding; | |||
/** | |||
* Class representing an /Encoding object. | |||
* | |||
@@ -73,6 +76,38 @@ public class PDFEncoding extends PDFDictionary { | |||
} | |||
} | |||
/** | |||
* Creates a PDFEncoding instance from a CodePointMapping instance. | |||
* @param encoding the code point mapping (encoding) | |||
* @param fontName ... | |||
* @return the PDF Encoding dictionary (or a String with the predefined encoding) | |||
*/ | |||
static Object createPDFEncoding(SingleByteEncoding encoding, String fontName) { | |||
//If encoding type is null, return null which causes /Encoding to be omitted. | |||
if (encoding == null) { | |||
return null; | |||
} | |||
String encodingName = null; | |||
SingleByteEncoding baseEncoding; | |||
if (fontName.indexOf("Symbol") >= 0) { | |||
baseEncoding = CodePointMapping.getMapping(CodePointMapping.SYMBOL_ENCODING); | |||
encodingName = baseEncoding.getName(); | |||
} else { | |||
baseEncoding = CodePointMapping.getMapping(CodePointMapping.STANDARD_ENCODING); | |||
} | |||
PDFEncoding pdfEncoding = new PDFEncoding(encodingName); | |||
PDFEncoding.DifferencesBuilder builder = pdfEncoding.createDifferencesBuilder(); | |||
PDFArray differences = builder.buildDifferencesArray(baseEncoding, encoding); | |||
// TODO This method should not be returning an Object with two different outcomes | |||
// resulting in subsequent `if (X instanceof Y)` statements. | |||
if (differences.length() > 0) { | |||
pdfEncoding.setDifferences(differences); | |||
return pdfEncoding; | |||
} else { | |||
return encodingName; | |||
} | |||
} | |||
/** | |||
* Indicates whether a given encoding is one of the predefined encodings. | |||
* @param name the encoding name (ex. "StandardEncoding") | |||
@@ -82,6 +117,15 @@ public class PDFEncoding extends PDFDictionary { | |||
return PREDEFINED_ENCODINGS.contains(name); | |||
} | |||
/** | |||
* Indicates whether the given encoding type is that of standard encoding | |||
* @param name The encoding name | |||
* @return Returns true if it is of type standard encoding | |||
*/ | |||
static boolean hasStandardEncoding(String encodingName) { | |||
return encodingName.equals(STANDARD_ENCODING); | |||
} | |||
/** | |||
* Creates and returns a new DifferencesBuilder instance for constructing the Differences | |||
* array. | |||
@@ -104,18 +148,44 @@ public class PDFEncoding extends PDFDictionary { | |||
*/ | |||
public class DifferencesBuilder { | |||
private PDFArray differences = new PDFArray(); | |||
private int currentCode = -1; | |||
/** | |||
* Creates an array containing the differences between two single-byte. | |||
* font encodings. | |||
* @param encodingA The first single-byte encoding | |||
* @param encodingB The second single-byte encoding | |||
* @return The PDFArray of differences between encodings | |||
*/ | |||
public PDFArray buildDifferencesArray(SingleByteEncoding encodingA, | |||
SingleByteEncoding encodingB) { | |||
PDFArray differences = new PDFArray(); | |||
int start = -1; | |||
String[] baseNames = encodingA.getCharNameMap(); | |||
String[] charNameMap = encodingB.getCharNameMap(); | |||
for (int i = 0, ci = charNameMap.length; i < ci; i++) { | |||
String basec = baseNames[i]; | |||
String c = charNameMap[i]; | |||
if (!basec.equals(c)) { | |||
if (start != i) { | |||
addDifference(i, differences); | |||
start = i; | |||
} | |||
addName(c, differences); | |||
start++; | |||
} | |||
} | |||
return differences; | |||
} | |||
/** | |||
* Start a new difference. | |||
* @param code the starting code index inside the encoding | |||
* @return this builder instance | |||
*/ | |||
public DifferencesBuilder addDifference(int code) { | |||
private void addDifference(int code, PDFArray differences) { | |||
this.currentCode = code; | |||
this.differences.add(new Integer(code)); | |||
return this; | |||
differences.add(Integer.valueOf(code)); | |||
} | |||
/** | |||
@@ -123,28 +193,11 @@ public class PDFEncoding extends PDFDictionary { | |||
* @param name the character name | |||
* @return this builder instance | |||
*/ | |||
public DifferencesBuilder addName(String name) { | |||
private void addName(String name, PDFArray differences) { | |||
if (this.currentCode < 0) { | |||
throw new IllegalStateException("addDifference(int) must be called first"); | |||
} | |||
this.differences.add(new PDFName(name)); | |||
return this; | |||
} | |||
/** | |||
* Indicates whether any differences have been recorded. | |||
* @return true if there are differences. | |||
*/ | |||
public boolean hasDifferences() { | |||
return (this.differences.length() > 0); | |||
} | |||
/** | |||
* Creates and returns the PDFArray representing the Differences entry. | |||
* @return the Differences entry | |||
*/ | |||
public PDFArray toPDFArray() { | |||
return this.differences; | |||
differences.add(new PDFName(name)); | |||
} | |||
} | |||
@@ -44,9 +44,9 @@ import org.apache.xmlgraphics.java2d.color.NamedColorSpace; | |||
import org.apache.xmlgraphics.xmp.Metadata; | |||
import org.apache.fop.fonts.CIDFont; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CodePointMapping; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.FontDescriptor; | |||
import org.apache.fop.fonts.FontMetrics; | |||
import org.apache.fop.fonts.FontType; | |||
@@ -1369,23 +1369,14 @@ public class PDFFactory { | |||
} else { | |||
cidMetrics = (CIDFont)metrics; | |||
} | |||
PDFCIDSystemInfo sysInfo | |||
= new PDFCIDSystemInfo(cidMetrics.getRegistry(), | |||
cidMetrics.getOrdering(), | |||
cidMetrics.getSupplement()); | |||
PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, | |||
cidMetrics.getCIDType(), | |||
cidMetrics.getDefaultWidth(), | |||
getSubsetWidths(cidMetrics), sysInfo, | |||
(PDFCIDFontDescriptor)pdfdesc); | |||
PDFCIDSystemInfo sysInfo = new PDFCIDSystemInfo(cidMetrics.getRegistry(), | |||
cidMetrics.getOrdering(), cidMetrics.getSupplement()); | |||
PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, cidMetrics.getCIDType(), | |||
cidMetrics.getDefaultWidth(), getFontWidths(cidMetrics), sysInfo, | |||
(PDFCIDFontDescriptor) pdfdesc); | |||
getDocument().registerObject(cidFont); | |||
PDFCMap cmap = new PDFToUnicodeCMap( | |||
cidMetrics.getCIDSubset().getSubsetChars(), | |||
"fop-ucs-H", | |||
new PDFCIDSystemInfo("Adobe", | |||
"Identity", | |||
0), false); | |||
PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics.getCIDSet().getChars(), "fop-ucs-H", | |||
new PDFCIDSystemInfo("Adobe", "Identity", 0), false); | |||
getDocument().registerObject(cmap); | |||
((PDFFontType0)font).setCMAP(cmap); | |||
((PDFFontType0)font).setDescendantFonts(cidFont); | |||
@@ -1469,61 +1460,18 @@ public class PDFFactory { | |||
/** | |||
* Creates a PDFEncoding instance from a CodePointMapping instance. | |||
* @param encoding the code point mapping (encoding) | |||
* @param fontNameHint ... | |||
* @param fontName ... | |||
* @return the PDF Encoding dictionary (or a String with the predefined encoding) | |||
*/ | |||
public Object createPDFEncoding(SingleByteEncoding encoding, String fontNameHint) { | |||
SingleByteEncoding baseEncoding; | |||
if (fontNameHint.indexOf("Symbol") >= 0) { | |||
baseEncoding = CodePointMapping.getMapping( | |||
CodePointMapping.SYMBOL_ENCODING); | |||
} else { | |||
baseEncoding = CodePointMapping.getMapping( | |||
CodePointMapping.STANDARD_ENCODING); | |||
} | |||
PDFEncoding pdfEncoding = new PDFEncoding(baseEncoding.getName()); | |||
PDFEncoding.DifferencesBuilder builder | |||
= pdfEncoding.createDifferencesBuilder(); | |||
int start = -1; | |||
String[] baseNames = baseEncoding.getCharNameMap(); | |||
String[] charNameMap = encoding.getCharNameMap(); | |||
for (int i = 0, ci = charNameMap.length; i < ci; i++) { | |||
String basec = baseNames[i]; | |||
String c = charNameMap[i]; | |||
if (!basec.equals(c)) { | |||
if (start != i) { | |||
builder.addDifference(i); | |||
start = i; | |||
} | |||
builder.addName(c); | |||
start++; | |||
} | |||
} | |||
if (builder.hasDifferences()) { | |||
pdfEncoding.setDifferences(builder.toPDFArray()); | |||
return pdfEncoding; | |||
} else { | |||
return baseEncoding.getName(); | |||
} | |||
public Object createPDFEncoding(SingleByteEncoding encoding, String fontName) { | |||
return PDFEncoding.createPDFEncoding(encoding, fontName); | |||
} | |||
/** | |||
* Creates and returns a width array with the widths of all the characters in the subset. | |||
* @param cidFont the font | |||
* @return the width array | |||
*/ | |||
public PDFWArray getSubsetWidths(CIDFont cidFont) { | |||
private PDFWArray getFontWidths(CIDFont cidFont) { | |||
// Create widths for reencoded chars | |||
PDFWArray warray = new PDFWArray(); | |||
int[] widths = cidFont.getWidths(); | |||
CIDSubset subset = cidFont.getCIDSubset(); | |||
int[] tmpWidth = new int[subset.getSubsetSize()]; | |||
for (int i = 0, c = subset.getSubsetSize(); i < c; i++) { | |||
int nwx = Math.max(0, subset.getGlyphIndexForSubsetIndex(i)); | |||
tmpWidth[i] = widths[nwx]; | |||
} | |||
warray.addEntry(0, tmpWidth); | |||
int[] widths = cidFont.getCIDSet().getWidths(); | |||
warray.addEntry(0, widths); | |||
return warray; | |||
} | |||
@@ -1591,13 +1539,13 @@ public class PDFFactory { | |||
} | |||
private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) { | |||
BitSet cidSubset = cidFont.getCIDSubset().getGlyphIndexBitSet(); | |||
PDFStream cidSet = makeStream(null, true); | |||
ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1); | |||
BitSet cidSet = cidFont.getCIDSet().getGlyphIndices(); | |||
PDFStream pdfStream = makeStream(null, true); | |||
ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSet.length() / 8 + 1); | |||
int value = 0; | |||
for (int i = 0, c = cidSubset.length(); i < c; i++) { | |||
for (int i = 0, c = cidSet.length(); i < c; i++) { | |||
int shift = i % 8; | |||
boolean b = cidSubset.get(i); | |||
boolean b = cidSet.get(i); | |||
if (b) { | |||
value |= 1 << 7 - shift; | |||
} | |||
@@ -1608,8 +1556,8 @@ public class PDFFactory { | |||
} | |||
baout.write(value); | |||
try { | |||
cidSet.setData(baout.toByteArray()); | |||
descriptor.setCIDSet(cidSet); | |||
pdfStream.setData(baout.toByteArray()); | |||
descriptor.setCIDSet(pdfStream); | |||
} catch (IOException ioe) { | |||
log.error( | |||
"Failed to write CIDSet [" + cidFont + "] " | |||
@@ -1640,14 +1588,16 @@ public class PDFFactory { | |||
if (desc.getFontType() == FontType.TYPE0) { | |||
MultiByteFont mbfont = (MultiByteFont) font; | |||
FontFileReader reader = new FontFileReader(in); | |||
TTFSubSetFile subset = new TTFSubSetFile(); | |||
subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); | |||
byte[] subsetFont = subset.getFontSubset(); | |||
// Only TrueType CID fonts are supported now | |||
embeddedFont = new PDFTTFStream(subsetFont.length); | |||
((PDFTTFStream) embeddedFont).setData(subsetFont, subsetFont.length); | |||
byte[] fontBytes; | |||
if (font.getEmbeddingMode() == EmbeddingMode.FULL) { | |||
fontBytes = reader.getAllBytes(); | |||
} else { | |||
TTFSubSetFile ttfFile = new TTFSubSetFile(); | |||
ttfFile.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); | |||
fontBytes = ttfFile.getFontSubset(); | |||
} | |||
embeddedFont = new PDFTTFStream(fontBytes.length); | |||
((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length); | |||
} else if (desc.getFontType() == FontType.TYPE1) { | |||
PFBParser parser = new PFBParser(); | |||
PFBData pfb = parser.parsePFB(in); |
@@ -70,7 +70,7 @@ public class PDFFont extends PDFDictionary { | |||
* @param encoding the encoding | |||
*/ | |||
public void setEncoding(String encoding) { | |||
if (encoding != null) { | |||
if (encoding != null && !PDFEncoding.hasStandardEncoding(encoding)) { | |||
put("Encoding", new PDFName(encoding)); | |||
} | |||
} |
@@ -202,7 +202,8 @@ public class PDFProfile { | |||
PDFDictionary markInfo = getDocument().getRoot().getMarkInfo(); | |||
if (markInfo == null) { | |||
throw new PDFConformanceException(format( | |||
"{0} requires the MarkInfo dictionary to be present", getPDFAMode())); | |||
"{0} requires that the accessibility option in the configuration file be enabled", | |||
getPDFAMode())); | |||
} | |||
if (!Boolean.TRUE.equals(markInfo.get("Marked"))) { | |||
throw new PDFConformanceException(format(err, |
@@ -26,14 +26,16 @@ import java.util.List; | |||
import java.util.Locale; | |||
import org.apache.fop.accessibility.StructureTreeElement; | |||
import org.apache.fop.pdf.StandardStructureAttributes.Table; | |||
import org.apache.fop.util.LanguageTags; | |||
/** | |||
* Class representing a PDF Structure Element. | |||
*/ | |||
public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject { | |||
public class PDFStructElem extends StructureHierarchyMember | |||
implements StructureTreeElement, CompressedObject { | |||
private static final PDFName TABLE = new PDFName("Table"); | |||
private StructureType structureType; | |||
private PDFStructElem parentElement; | |||
@@ -50,12 +52,17 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
* @param parent parent of this element | |||
* @param structureType the structure type of this element | |||
*/ | |||
PDFStructElem(PDFObject parent, PDFName structureType) { | |||
public PDFStructElem(PDFObject parent, StructureType structureType) { | |||
this(parent); | |||
this.structureType = structureType; | |||
put("S", structureType.getName()); | |||
setParent(parent); | |||
} | |||
private PDFStructElem(PDFObject parent) { | |||
if (parent instanceof PDFStructElem) { | |||
parentElement = (PDFStructElem) parent; | |||
} | |||
put("S", structureType); | |||
setParent(parent); | |||
} | |||
/** | |||
@@ -80,6 +87,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
* | |||
* @param kid element to be added | |||
*/ | |||
@Override | |||
public void addKid(PDFObject kid) { | |||
if (kids == null) { | |||
kids = new ArrayList<PDFObject>(); | |||
@@ -113,8 +121,8 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
* | |||
* @return the value of the S entry | |||
*/ | |||
public PDFName getStructureType() { | |||
return (PDFName) get("S"); | |||
public StructureType getStructureType() { | |||
return structureType; | |||
} | |||
/** | |||
@@ -201,7 +209,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
private void setTableAttributeRowColumnSpan(String typeSpan, int span) { | |||
PDFDictionary attribute = new PDFDictionary(); | |||
attribute.put("O", TABLE); | |||
attribute.put("O", Table.NAME); | |||
attribute.put(typeSpan, span); | |||
if (attributes == null) { | |||
attributes = new ArrayList<PDFDictionary>(2); | |||
@@ -228,13 +236,8 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
} | |||
} | |||
/** | |||
* Constructor | |||
* @param parent - | |||
* @param name - | |||
*/ | |||
public Placeholder(PDFObject parent, String name) { | |||
super(parent, new PDFName(name)); | |||
public Placeholder(PDFObject parent) { | |||
super(parent); | |||
} | |||
} | |||
@@ -22,7 +22,7 @@ package org.apache.fop.pdf; | |||
/** | |||
* Class representing a PDF /StructTreeRoot dictionary. | |||
*/ | |||
public class PDFStructTreeRoot extends PDFDictionary { | |||
public class PDFStructTreeRoot extends StructureHierarchyMember { | |||
/** | |||
* Creates a new /StructTreeRoot dictionary. | |||
@@ -49,6 +49,7 @@ public class PDFStructTreeRoot extends PDFDictionary { | |||
* | |||
* @param kid an object to be added to the K entry | |||
*/ | |||
@Override | |||
public void addKid(PDFObject kid) { | |||
getKids().add(kid); | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.pdf; | |||
/** | |||
* Standard attributes, as defined in section 10.7.5 of the PDF Reference, Fourth edition (PDF 1.5). | |||
*/ | |||
public final class StandardStructureAttributes { | |||
public static final class Table { | |||
/** | |||
* The name to use as an attribute owner. This is the value of the 'O' entry in | |||
* the attribute's dictionary. | |||
*/ | |||
public static final PDFName NAME = new PDFName("Table"); | |||
public static enum Scope { | |||
ROW("Row"), | |||
COLUMN("Column"), | |||
BOTH("Both"); | |||
private final PDFName name; | |||
private Scope(String name) { | |||
this.name = new PDFName(name); | |||
} | |||
/** | |||
* Returns the name of this attribute. | |||
* | |||
* @return a name suitable for use as a value in the attribute's dictionary | |||
*/ | |||
public PDFName getName() { | |||
return name; | |||
} | |||
/** | |||
* Sets the given scope on the given table header element. | |||
*/ | |||
static void addScopeAttribute(PDFStructElem th, Scope scope) { | |||
PDFDictionary scopeAttribute = new PDFDictionary(); | |||
scopeAttribute.put("O", Table.NAME); | |||
scopeAttribute.put("Scope", scope.getName()); | |||
th.put("A", scopeAttribute); | |||
} | |||
} | |||
} | |||
private StandardStructureAttributes() { } | |||
} |
@@ -0,0 +1,137 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.pdf; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* Standard structure types, as defined in section 10.7.4 of the PDF Reference, Fourth Edition (PDF 1.5). | |||
*/ | |||
public final class StandardStructureTypes { | |||
public static final class Grouping { | |||
public static final StructureType DOCUMENT = new StructureTypeImpl("Document"); | |||
public static final StructureType PART = new StructureTypeImpl("Part"); | |||
public static final StructureType ART = new StructureTypeImpl("Art"); | |||
public static final StructureType SECT = new StructureTypeImpl("Sect"); | |||
public static final StructureType DIV = new StructureTypeImpl("Div"); | |||
public static final StructureType BLOCK_QUOTE = new StructureTypeImpl("BlockQuote"); | |||
public static final StructureType CAPTION = new StructureTypeImpl("Caption"); | |||
public static final StructureType TOC = new StructureTypeImpl("TOC"); | |||
public static final StructureType TOCI = new StructureTypeImpl("TOCI"); | |||
public static final StructureType INDEX = new StructureTypeImpl("Index"); | |||
public static final StructureType NON_STRUCT = new StructureTypeImpl("NonStruct"); | |||
public static final StructureType PRIVATE = new StructureTypeImpl("Private"); | |||
} | |||
public static final class Paragraphlike { | |||
public static final StructureType H = new StructureTypeImpl("H"); | |||
public static final StructureType H1 = new StructureTypeImpl("H1"); | |||
public static final StructureType H2 = new StructureTypeImpl("H2"); | |||
public static final StructureType H3 = new StructureTypeImpl("H3"); | |||
public static final StructureType H4 = new StructureTypeImpl("H4"); | |||
public static final StructureType H5 = new StructureTypeImpl("H5"); | |||
public static final StructureType H6 = new StructureTypeImpl("H6"); | |||
public static final StructureType P = new StructureTypeImpl("P"); | |||
} | |||
public static final class List { | |||
public static final StructureType L = new StructureTypeImpl("L"); | |||
public static final StructureType LI = new StructureTypeImpl("LI"); | |||
public static final StructureType LBL = new StructureTypeImpl("Lbl"); | |||
public static final StructureType LBODY = new StructureTypeImpl("LBody"); | |||
} | |||
public static final class Table { | |||
public static final StructureType TABLE = new StructureTypeImpl("Table"); | |||
public static final StructureType TR = new StructureTypeImpl("TR"); | |||
public static final StructureType TH = new StructureTypeImpl("TH"); | |||
public static final StructureType TD = new StructureTypeImpl("TD"); | |||
public static final StructureType THEAD = new StructureTypeImpl("THead"); | |||
public static final StructureType TBODY = new StructureTypeImpl("TBody"); | |||
public static final StructureType TFOOT = new StructureTypeImpl("TFoot"); | |||
} | |||
public static final class InlineLevelStructure { | |||
public static final StructureType SPAN = new StructureTypeImpl("Span"); | |||
public static final StructureType QUOTE = new StructureTypeImpl("Quote"); | |||
public static final StructureType NOTE = new StructureTypeImpl("Note"); | |||
public static final StructureType REFERENCE = new StructureTypeImpl("Reference"); | |||
public static final StructureType BIB_ENTRY = new StructureTypeImpl("BibEntry"); | |||
public static final StructureType CODE = new StructureTypeImpl("Code"); | |||
public static final StructureType LINK = new StructureTypeImpl("Link"); | |||
public static final StructureType ANNOT = new StructureTypeImpl("Annot"); | |||
} | |||
public static final class RubyOrWarichu { | |||
public static final StructureType RUBY = new StructureTypeImpl("Ruby"); | |||
public static final StructureType RB = new StructureTypeImpl("RB"); | |||
public static final StructureType RT = new StructureTypeImpl("RT"); | |||
public static final StructureType RP = new StructureTypeImpl("RP"); | |||
public static final StructureType WARICHU = new StructureTypeImpl("Warichu"); | |||
public static final StructureType WT = new StructureTypeImpl("WT"); | |||
public static final StructureType WP = new StructureTypeImpl("WP"); | |||
} | |||
public static final class Illustration { | |||
public static final StructureType FIGURE = new StructureTypeImpl("Figure"); | |||
public static final StructureType FORMULA = new StructureTypeImpl("Formula"); | |||
public static final StructureType FORM = new StructureTypeImpl("Form"); | |||
} | |||
private static class StructureTypeImpl implements StructureType { | |||
private final PDFName name; | |||
protected StructureTypeImpl(String name) { | |||
this.name = new PDFName(name); | |||
StandardStructureTypes.STRUCTURE_TYPES.put(name, this); | |||
} | |||
public PDFName getName() { | |||
return name; | |||
} | |||
@Override | |||
public String toString() { | |||
return name.toString().substring(1); | |||
} | |||
} | |||
private static final Map<String, StructureType> STRUCTURE_TYPES = new HashMap<String, StructureType>(); | |||
private StandardStructureTypes() { } | |||
/** | |||
* Returns the standard structure type of the given name. | |||
* | |||
* @param name the name of a structure type, case sensitive. For example, Document, | |||
* Sect, H1, etc. | |||
* @return the corresponding {@code StructureType} instance, or {@code null} if the given | |||
* name does not correspond to a standard structure type | |||
*/ | |||
public static StructureType get(String name) { | |||
return STRUCTURE_TYPES.get(name); | |||
} | |||
} |
@@ -17,48 +17,21 @@ | |||
/* $Id$ */ | |||
package org.apache.fop.afp.modca; | |||
package org.apache.fop.pdf; | |||
/** | |||
* The TagLogicalElementBean provides a bean for holding the attributes of | |||
* a tag logical element as key value pairs. | |||
* <p/> | |||
* An element in the document's structure tree. This can be either the structure tree root | |||
* or a structure element. | |||
* | |||
* @see "Section 10.6, <q>Logical Structure</q> of the PDF Reference, 4th edition (PDF 1.5)" | |||
*/ | |||
public class TagLogicalElementBean { | |||
/** The key attribute */ | |||
private String key; | |||
/** The value attribute */ | |||
private String value; | |||
/** | |||
* Constructor for the TagLogicalElementBean. | |||
* | |||
* @param key the key attribute | |||
* @param value the value attribute | |||
*/ | |||
public TagLogicalElementBean(String key, String value) { | |||
this.key = key; | |||
this.value = value; | |||
} | |||
/** | |||
* Getter for the key attribute. | |||
* | |||
* @return the key | |||
*/ | |||
public String getKey() { | |||
return key; | |||
} | |||
public abstract class StructureHierarchyMember extends PDFDictionary { | |||
/** | |||
* Getter for the value attribute. | |||
* Adds the given object to the array of kids. | |||
* | |||
* @return the value | |||
* @param kid an object to be added to the K entry | |||
*/ | |||
public String getValue() { | |||
return value; | |||
} | |||
public abstract void addKid(PDFObject kid); | |||
} |
@@ -0,0 +1,34 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.pdf; | |||
/** | |||
* A structure type, as defined in Section 10.6.2 of the PDF Reference, fourth edition (PDF 1.5). | |||
*/ | |||
public interface StructureType { | |||
/** | |||
* Returns the name of this structure type. | |||
* | |||
* @return the name object identifying this structure type | |||
*/ | |||
PDFName getName(); | |||
} |
@@ -19,6 +19,8 @@ | |||
package org.apache.fop.pdf; | |||
import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope; | |||
/** | |||
* An abstraction that controls the mutability of the PDF version for a document. | |||
*/ | |||
@@ -47,6 +49,8 @@ public abstract class VersionController { | |||
*/ | |||
public abstract void setPDFVersion(Version version); | |||
abstract void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope); | |||
@Override | |||
public String toString() { | |||
return version.toString(); | |||
@@ -67,6 +71,13 @@ public abstract class VersionController { | |||
public void setPDFVersion(Version version) { | |||
throw new IllegalStateException("Cannot change the version of this PDF document."); | |||
} | |||
@Override | |||
void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope) { | |||
if (super.version.compareTo(Version.V1_4) > 0) { | |||
Scope.addScopeAttribute(th, scope); | |||
} | |||
} | |||
} | |||
/** | |||
@@ -91,6 +102,12 @@ public abstract class VersionController { | |||
doc.getRoot().setVersion(version); | |||
} | |||
} | |||
@Override | |||
void addTableHeaderScopeAttribute(PDFStructElem th, Scope scope) { | |||
setPDFVersion(Version.V1_5); | |||
Scope.addScopeAttribute(th, scope); | |||
} | |||
} | |||
/** |
@@ -318,7 +318,8 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler | |||
case IN_PAGE_HEADER: | |||
String name = aps.getName(); | |||
String value = aps.getValue(); | |||
dataStream.createTagLogicalElement(name, value); | |||
int encoding = aps.getEncoding(); | |||
dataStream.createTagLogicalElement(name, value, encoding); | |||
break; | |||
default: | |||
throw new IFException( |
@@ -329,8 +329,8 @@ public final class AFPFontConfig implements FontConfig { | |||
AFPResourceAccessor accessor = getAccessor(resourceResolver); | |||
CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().buildDBCS( | |||
characterset, super.codePage, super.encoding, charsetType, accessor, eventProducer); | |||
return getFontInfo(new DoubleByteFont(super.codePage, super.embeddable, characterSet), | |||
this); | |||
return getFontInfo(new DoubleByteFont(super.codePage, super.embeddable, characterSet, | |||
eventProducer), this); | |||
} | |||
} | |||
@@ -365,7 +365,8 @@ public final class AFPFontConfig implements FontConfig { | |||
characterSet = CharacterSetBuilder.getSingleByteInstance().buildSBCS( | |||
characterset, super.codePage, super.encoding, accessor, eventProducer); | |||
} | |||
return getFontInfo(new OutlineFont(super.name, super.embeddable, characterSet), this); | |||
return getFontInfo(new OutlineFont(super.name, super.embeddable, characterSet, | |||
eventProducer), this); | |||
} | |||
} | |||
@@ -144,6 +144,12 @@ public class AFPExtensionHandler extends DefaultHandler | |||
if (placement != null && placement.length() > 0) { | |||
pageSetupExtn.setPlacement(ExtensionPlacement.fromXMLValue(placement)); | |||
} | |||
String encoding = lastAttributes.getValue("encoding"); | |||
if (encoding != null && pageSetupExtn != null) { | |||
pageSetupExtn.setEncoding(Integer.parseInt(encoding)); | |||
} | |||
if (content.length() > 0 && pageSetupExtn != null) { | |||
pageSetupExtn.setContent(content.toString()); | |||
content.setLength(0); //Reset text buffer (see characters()) |
@@ -23,6 +23,8 @@ import org.xml.sax.ContentHandler; | |||
import org.xml.sax.SAXException; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
import org.apache.fop.afp.modca.TagLogicalElement; | |||
/** | |||
* This is the pass-through value object for the AFP extension. | |||
*/ | |||
@@ -47,6 +49,27 @@ public class AFPPageSetup extends AFPExtensionAttachment { | |||
/** defines where to place the extension in the generated file */ | |||
protected ExtensionPlacement placement = ExtensionPlacement.DEFAULT; | |||
/** | |||
* the CCSID character set encoding | |||
*/ | |||
protected int encoding = TagLogicalElement.State.ENCODING_NONE; | |||
/** | |||
* | |||
* @return CCSID character set encoding | |||
*/ | |||
public int getEncoding() { | |||
return encoding; | |||
} | |||
/** | |||
* | |||
* @param encoding CCSID character set encoding | |||
*/ | |||
public void setEncoding(int encoding) { | |||
this.encoding = encoding; | |||
} | |||
/** | |||
* Default constructor. | |||
* |
@@ -35,6 +35,7 @@ import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
*/ | |||
public class AFPPageSetupElement extends AbstractAFPExtensionObject { | |||
private static final String ATT_ENCODING = "encoding"; | |||
private static final String ATT_SRC = "src"; | |||
/** | |||
@@ -105,6 +106,16 @@ public class AFPPageSetupElement extends AbstractAFPExtensionObject { | |||
} else { | |||
missingPropertyError(AFPPageSetup.ATT_VALUE); | |||
} | |||
attr = attlist.getValue(ATT_ENCODING); | |||
if (attr != null) { | |||
try { | |||
pageSetup.setEncoding(Integer.parseInt(attr)); | |||
} catch (NumberFormatException nfe) { | |||
invalidPropertyValueError(ATT_ENCODING, attr, nfe); | |||
} | |||
} | |||
} | |||
String placement = attlist.getValue(AFPPageSetup.ATT_PLACEMENT); | |||
if (placement != null && placement.length() > 0) { |
@@ -84,7 +84,7 @@ public abstract class AbstractBitmapDocumentHandler extends AbstractBinaryWritin | |||
super(context); | |||
//Set target resolution | |||
int dpi = Math.round(context.getUserAgent().getTargetResolution()); | |||
getSettings().getWriterParams().setResolution(dpi); | |||
getSettings().setResolution(dpi); | |||
Map renderingOptions = getUserAgent().getRendererOptions(); | |||
setTargetBitmapSize((Dimension)renderingOptions.get(TARGET_BITMAP_SIZE)); |
@@ -107,4 +107,27 @@ public class BitmapRenderingSettings extends Java2DRenderingSettings { | |||
return this.qualityRendering; | |||
} | |||
/** | |||
* Sets the compression method for the image writer. | |||
* @param compressionMethod the compression method name | |||
*/ | |||
public void setCompressionMethod(String compressionMethod) { | |||
writerParams.setCompressionMethod(compressionMethod); | |||
} | |||
/** | |||
* Returns the compression method being used by the image writer. | |||
* @return the compression method in use | |||
*/ | |||
public String getCompressionMethod() { | |||
return writerParams.getCompressionMethod(); | |||
} | |||
/** | |||
* Sets the resolution of the output image. | |||
* @param dpi the dots-per-inch of the image | |||
*/ | |||
public void setResolution(int dpi) { | |||
writerParams.setResolution(dpi); | |||
} | |||
} |
@@ -0,0 +1,98 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.bitmap; | |||
import java.awt.image.BufferedImage; | |||
/** | |||
* Compression constants for TIFF image output. | |||
*/ | |||
public enum TIFFCompressionValue { | |||
/** No compression */ | |||
NONE("NONE"), | |||
/** JPEG compression */ | |||
JPEG("JPEG"), | |||
/** Packbits (RLE) compression */ | |||
PACKBITS("PackBits"), | |||
/** Deflate compression */ | |||
DEFLATE("Deflate"), | |||
/** LZW compression */ | |||
LZW("LZW"), | |||
/** ZLib compression */ | |||
ZLIB("ZLib"), | |||
/** CCITT Group 3 (T.4) compression */ | |||
CCITT_T4("CCITT T.4", BufferedImage.TYPE_BYTE_BINARY, true), | |||
/** CCITT Group 4 (T.6) compression */ | |||
CCITT_T6("CCITT T.6", BufferedImage.TYPE_BYTE_BINARY, true); | |||
private final String name; | |||
private final int imageType; | |||
private boolean isCcitt; | |||
private TIFFCompressionValue(String name, int imageType, boolean isCcitt) { | |||
this.name = name; | |||
this.imageType = imageType; | |||
this.isCcitt = isCcitt; | |||
} | |||
private TIFFCompressionValue(String name) { | |||
this(name, BufferedImage.TYPE_INT_ARGB, false); | |||
} | |||
/** | |||
* Returns the name of this compression type. | |||
* @return the compression name | |||
*/ | |||
String getName() { | |||
return name; | |||
} | |||
/** | |||
* Returns an image type for this compression type, a constant from {@link BufferedImage} e.g. | |||
* {@link BufferedImage#TYPE_INT_ARGB} for {@link #ZLIB} | |||
* @return the image type | |||
*/ | |||
int getImageType() { | |||
return imageType; | |||
} | |||
/** | |||
* Returns whether or not this compression type is a CCITT type. | |||
* @return true if the compression type is CCITT | |||
*/ | |||
boolean hasCCITTCompression() { | |||
return isCcitt; | |||
} | |||
/** | |||
* Return the TIFF compression constant given the string representing the type. In the case that | |||
* the name doesn't match any of the compression values, <code>null</code> is returned. | |||
* @param name the compression type name | |||
* @return the compression constant | |||
*/ | |||
static TIFFCompressionValue getType(String name) { | |||
for (TIFFCompressionValue tiffConst : TIFFCompressionValue.values()) { | |||
if (tiffConst.name.equalsIgnoreCase(name)) { | |||
return tiffConst; | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -1,61 +0,0 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.bitmap; | |||
/** | |||
* Constants for TIFF output. | |||
*/ | |||
public enum TIFFCompressionValues { | |||
/** No compression */ | |||
NONE("NONE"), | |||
/** JPEG compression */ | |||
JPEG("JPEG"), | |||
/** Packbits (RLE) compression */ | |||
PACKBITS("PackBits"), | |||
/** Deflate compression */ | |||
DEFLATE("Deflate"), | |||
/** LZW compression */ | |||
LZW("LZW"), | |||
/** ZLib compression */ | |||
ZLIB("ZLib"), | |||
/** CCITT Group 4 (T.6) compression */ | |||
CCITT_T6("CCITT T.6"), //CCITT Group 4 | |||
/** CCITT Group 3 (T.4) compression */ | |||
CCITT_T4("CCITT T.4"); //CCITT Group 3 | |||
private final String name; | |||
private TIFFCompressionValues(String name) { | |||
this.name = name; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public static TIFFCompressionValues getValue(String name) { | |||
for (TIFFCompressionValues tiffConst : TIFFCompressionValues.values()) { | |||
if (tiffConst.name.equalsIgnoreCase(name)) { | |||
return tiffConst; | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -19,6 +19,8 @@ | |||
package org.apache.fop.render.bitmap; | |||
import org.apache.xmlgraphics.image.writer.ResolutionUnit; | |||
import org.apache.fop.apps.MimeConstants; | |||
import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; | |||
import org.apache.fop.render.intermediate.IFContext; | |||
@@ -32,6 +34,7 @@ public class TIFFDocumentHandler extends AbstractBitmapDocumentHandler { | |||
TIFFDocumentHandler(IFContext context) { | |||
super(context); | |||
getSettings().getWriterParams().setResolutionUnit(ResolutionUnit.CENTIMETER); | |||
} | |||
/** {@inheritDoc} */ |
@@ -38,7 +38,6 @@ import org.apache.commons.logging.Log; | |||
import org.apache.xmlgraphics.image.GraphicsUtil; | |||
import org.apache.xmlgraphics.image.rendered.FormatRed; | |||
import org.apache.xmlgraphics.image.writer.ImageWriter; | |||
import org.apache.xmlgraphics.image.writer.ImageWriterParams; | |||
import org.apache.xmlgraphics.image.writer.ImageWriterRegistry; | |||
import org.apache.xmlgraphics.image.writer.MultiImageWriter; | |||
@@ -47,9 +46,9 @@ import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.MimeConstants; | |||
import org.apache.fop.render.java2d.Java2DRenderer; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T4; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T6; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.PACKBITS; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T4; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T6; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValue.PACKBITS; | |||
/** | |||
* <p> | |||
@@ -74,11 +73,7 @@ import static org.apache.fop.render.bitmap.TIFFCompressionValues.PACKBITS; | |||
*/ | |||
public class TIFFRenderer extends Java2DRenderer { | |||
/** ImageWriter parameters */ | |||
private ImageWriterParams writerParams; | |||
/** Image Type as parameter for the BufferedImage constructor (see BufferedImage.TYPE_*) */ | |||
private int bufferedImageType = BufferedImage.TYPE_INT_ARGB; | |||
private BitmapRenderingSettings imageSettings; | |||
private OutputStream outputStream; | |||
@@ -94,11 +89,11 @@ public class TIFFRenderer extends Java2DRenderer { | |||
*/ | |||
public TIFFRenderer(FOUserAgent userAgent) { | |||
super(userAgent); | |||
writerParams = new ImageWriterParams(); | |||
writerParams.setCompressionMethod(PACKBITS.getName()); | |||
imageSettings = new BitmapRenderingSettings(); | |||
imageSettings.setCompressionMethod(PACKBITS.getName()); | |||
imageSettings.setBufferedImageType(BufferedImage.TYPE_INT_ARGB); | |||
int dpi = Math.round(userAgent.getTargetResolution()); | |||
writerParams.setResolution(dpi); | |||
imageSettings.setResolution(dpi); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -129,7 +124,7 @@ public class TIFFRenderer extends Java2DRenderer { | |||
// Write all pages/images | |||
while (pageImagesItr.hasNext()) { | |||
RenderedImage img = (RenderedImage) pageImagesItr.next(); | |||
multiWriter.writeImage(img, writerParams); | |||
multiWriter.writeImage(img, imageSettings.getWriterParams()); | |||
} | |||
} finally { | |||
multiWriter.close(); | |||
@@ -139,7 +134,7 @@ public class TIFFRenderer extends Java2DRenderer { | |||
if (pageImagesItr.hasNext()) { | |||
renderedImage = (RenderedImage) pageImagesItr.next(); | |||
} | |||
writer.writeImage(renderedImage, outputStream, writerParams); | |||
writer.writeImage(renderedImage, outputStream, imageSettings.getWriterParams()); | |||
if (pageImagesItr.hasNext()) { | |||
BitmapRendererEventProducer eventProducer | |||
= BitmapRendererEventProducer.Provider.get( | |||
@@ -156,7 +151,7 @@ public class TIFFRenderer extends Java2DRenderer { | |||
/** {@inheritDoc} */ | |||
protected BufferedImage getBufferedImage(int bitmapWidth, int bitmapHeight) { | |||
return new BufferedImage(bitmapWidth, bitmapHeight, bufferedImageType); | |||
return new BufferedImage(bitmapWidth, bitmapHeight, imageSettings.getBufferedImageType()); | |||
} | |||
/** Private inner class to lazy page rendering. */ | |||
@@ -195,7 +190,7 @@ public class TIFFRenderer extends Java2DRenderer { | |||
throw new NoSuchElementException(e.getMessage()); | |||
} | |||
TIFFCompressionValues compression = TIFFCompressionValues.getValue(writerParams.getCompressionMethod()); | |||
TIFFCompressionValue compression = TIFFCompressionValue.getType(imageSettings.getCompressionMethod()); | |||
if (compression == CCITT_T4 || compression == CCITT_T6) { | |||
return pageImage; | |||
} else { | |||
@@ -226,11 +221,14 @@ public class TIFFRenderer extends Java2DRenderer { | |||
/** @param bufferedImageType an image type */ | |||
public void setBufferedImageType(int bufferedImageType) { | |||
this.bufferedImageType = bufferedImageType; | |||
imageSettings.setBufferedImageType(bufferedImageType); | |||
} | |||
/** @return image writer parameters */ | |||
public ImageWriterParams getWriterParams() { | |||
return writerParams; | |||
/** | |||
* Returns the settings for the image rendering. | |||
* @return the image rendering settings | |||
*/ | |||
public BitmapRenderingSettings getRenderingSettings() { | |||
return imageSettings; | |||
} | |||
} |
@@ -23,21 +23,24 @@ import java.util.EnumMap; | |||
import org.apache.avalon.framework.configuration.Configuration; | |||
import org.apache.xmlgraphics.util.MimeConstants; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.MimeConstants; | |||
import org.apache.fop.fonts.DefaultFontConfig; | |||
import org.apache.fop.fonts.DefaultFontConfig.DefaultFontConfigParser; | |||
import org.apache.fop.render.RendererConfigOption; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValue.PACKBITS; | |||
/** | |||
* The renderer configuration object for the TIFF renderer. | |||
*/ | |||
public final class TIFFRendererConfig extends BitmapRendererConfig { | |||
public enum TIFFRendererOption implements RendererConfigOption { | |||
COMPRESSION("compression", TIFFCompressionValues.PACKBITS); | |||
COMPRESSION("compression", PACKBITS), | |||
/** option to encode one row per strip or a all rows in a single strip*/ | |||
SINGLE_STRIP("single-strip", Boolean.FALSE); | |||
private final String name; | |||
private final Object defaultValue; | |||
@@ -63,8 +66,16 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { | |||
super(fontConfig); | |||
} | |||
public TIFFCompressionValues getCompressionType() { | |||
return (TIFFCompressionValues) params.get(TIFFRendererOption.COMPRESSION); | |||
public TIFFCompressionValue getCompressionType() { | |||
return (TIFFCompressionValue) params.get(TIFFRendererOption.COMPRESSION); | |||
} | |||
/** | |||
* @return True if all rows are contained in a single strip, False each strip contains one row or null | |||
* if not set. | |||
*/ | |||
public Boolean isSingleStrip() { | |||
return (Boolean) params.get(TIFFRendererOption.SINGLE_STRIP); | |||
} | |||
/** | |||
@@ -92,9 +103,10 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { | |||
.parse(cfg, userAgent.validateStrictly())); | |||
super.build(config, userAgent, cfg); | |||
if (cfg != null) { | |||
setParam(TIFFRendererOption.COMPRESSION, | |||
TIFFCompressionValues.getValue(getValue(cfg, | |||
TIFFRendererOption.COMPRESSION))); | |||
setParam(TIFFRendererOption.COMPRESSION, | |||
TIFFCompressionValue.getType(getValue(cfg, TIFFRendererOption.COMPRESSION))); | |||
setParam(TIFFRendererOption.SINGLE_STRIP, Boolean.valueOf(getValue(cfg, | |||
TIFFRendererOption.SINGLE_STRIP))); | |||
} | |||
return config; | |||
} |
@@ -19,13 +19,9 @@ | |||
package org.apache.fop.render.bitmap; | |||
import java.awt.image.BufferedImage; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.image.writer.ImageWriterParams; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.render.Renderer; | |||
@@ -33,9 +29,7 @@ import org.apache.fop.render.RendererConfig.RendererConfigParser; | |||
import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; | |||
import org.apache.fop.render.intermediate.IFDocumentHandler; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T4; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T6; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValues.NONE; | |||
import static org.apache.fop.render.bitmap.TIFFCompressionValue.NONE; | |||
/** | |||
* TIFF Renderer configurator | |||
@@ -62,43 +56,40 @@ public class TIFFRendererConfigurator extends BitmapRendererConfigurator { | |||
final TIFFRendererConfig config = (TIFFRendererConfig) getRendererConfig(renderer); | |||
if (config != null) { | |||
TIFFRenderer tiffRenderer = (TIFFRenderer) renderer; | |||
//set compression | |||
tiffRenderer.setBufferedImageType(getCompressionType(config, tiffRenderer.getWriterParams())); | |||
setCompressionMethod(config.getCompressionType(), tiffRenderer.getRenderingSettings()); | |||
} | |||
super.configure(renderer); | |||
} | |||
private int getCompressionType(TIFFRendererConfig config, ImageWriterParams writerParms) | |||
throws FOPException { | |||
//Some compression formats need a special image format: | |||
TIFFCompressionValues compression = config.getCompressionType(); | |||
private void setCompressionMethod(TIFFCompressionValue compression, | |||
BitmapRenderingSettings settings) throws FOPException { | |||
if (compression != null) { | |||
if (compression != NONE) { | |||
writerParms.setCompressionMethod(compression.getName()); | |||
settings.setCompressionMethod(compression.getName()); | |||
} | |||
if (LOG.isInfoEnabled()) { | |||
LOG.info("TIFF compression set to " + compression.getName()); | |||
} | |||
if (compression.hasCCITTCompression()) { | |||
settings.setBufferedImageType(compression.getImageType()); | |||
} | |||
} | |||
return getBufferedImageTypeFor(compression); | |||
} | |||
private int getBufferedImageTypeFor(TIFFCompressionValues compressionType) { | |||
if (compressionType == CCITT_T6 || compressionType == CCITT_T4) { | |||
return BufferedImage.TYPE_BYTE_BINARY; | |||
} else { | |||
return BufferedImage.TYPE_INT_ARGB; | |||
} | |||
private boolean isSingleStrip(TIFFRendererConfig config) { | |||
Boolean singleRowPerStrip = config.isSingleStrip(); | |||
return singleRowPerStrip == null ? false : singleRowPerStrip; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public void configure(IFDocumentHandler documentHandler) throws FOPException { | |||
final TIFFRendererConfig tiffConfig = (TIFFRendererConfig) getRendererConfig(documentHandler); | |||
if (tiffConfig != null) { | |||
final TIFFRendererConfig config = (TIFFRendererConfig) getRendererConfig(documentHandler); | |||
if (config != null) { | |||
TIFFDocumentHandler tiffHandler = (TIFFDocumentHandler) documentHandler; | |||
BitmapRenderingSettings settings = tiffHandler.getSettings(); | |||
configure(documentHandler, settings, new TIFFRendererConfigParser()); | |||
settings.setBufferedImageType(getCompressionType(tiffConfig, settings.getWriterParams())); | |||
setCompressionMethod(config.getCompressionType(), settings); | |||
settings.getWriterParams().setSingleStrip(isSingleStrip(config)); | |||
} | |||
} | |||
@@ -1,244 +0,0 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.pdf; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.fop.events.EventBroadcaster; | |||
import org.apache.fop.pdf.PDFName; | |||
import org.apache.fop.pdf.PDFObject; | |||
import org.apache.fop.pdf.PDFStructElem; | |||
/** | |||
* This class provides the standard mappings from Formatting Objects to PDF structure types. | |||
*/ | |||
final class FOToPDFRoleMap { | |||
/** | |||
* Standard structure types defined by the PDF Reference, Fourth Edition (PDF 1.5). | |||
*/ | |||
private static final Map<String, PDFName> STANDARD_STRUCTURE_TYPES | |||
= new HashMap<String, PDFName>(); | |||
private static final Map<String, Mapper> DEFAULT_MAPPINGS | |||
= new java.util.HashMap<String, Mapper>(); | |||
private static final PDFName THEAD; | |||
private static final PDFName NON_STRUCT; | |||
static { | |||
// Create PDFNames for the standard structure types | |||
// Table 10.18: Grouping elements | |||
addStructureType("Document"); | |||
addStructureType("Part"); | |||
addStructureType("Art"); | |||
addStructureType("Sect"); | |||
addStructureType("Div"); | |||
addStructureType("BlockQuote"); | |||
addStructureType("Caption"); | |||
addStructureType("TOC"); | |||
addStructureType("TOCI"); | |||
addStructureType("Index"); | |||
addStructureType("NonStruct"); | |||
addStructureType("Private"); | |||
// Table 10.20: Paragraphlike elements | |||
addStructureType("H"); | |||
addStructureType("H1"); | |||
addStructureType("H2"); | |||
addStructureType("H3"); | |||
addStructureType("H4"); | |||
addStructureType("H5"); | |||
addStructureType("H6"); | |||
addStructureType("P"); | |||
// Table 10.21: List elements | |||
addStructureType("L"); | |||
addStructureType("LI"); | |||
addStructureType("Lbl"); | |||
addStructureType("LBody"); | |||
// Table 10.22: Table elements | |||
addStructureType("Table"); | |||
addStructureType("TR"); | |||
addStructureType("TH"); | |||
addStructureType("TD"); | |||
addStructureType("THead"); | |||
addStructureType("TBody"); | |||
addStructureType("TFoot"); | |||
// Table 10.23: Inline-level structure elements | |||
addStructureType("Span"); | |||
addStructureType("Quote"); | |||
addStructureType("Note"); | |||
addStructureType("Reference"); | |||
addStructureType("BibEntry"); | |||
addStructureType("Code"); | |||
addStructureType("Link"); | |||
addStructureType("Annot"); | |||
// Table 10.24: Ruby and Warichu elements | |||
addStructureType("Ruby"); | |||
addStructureType("RB"); | |||
addStructureType("RT"); | |||
addStructureType("RP"); | |||
addStructureType("Warichu"); | |||
addStructureType("WT"); | |||
addStructureType("WP"); | |||
// Table 10.25: Illustration elements | |||
addStructureType("Figure"); | |||
addStructureType("Formula"); | |||
addStructureType("Form"); | |||
NON_STRUCT = STANDARD_STRUCTURE_TYPES.get("NonStruct"); | |||
assert NON_STRUCT != null; | |||
THEAD = STANDARD_STRUCTURE_TYPES.get("THead"); | |||
assert THEAD != null; | |||
// Create the standard mappings | |||
// Declarations and Pagination and Layout Formatting Objects | |||
addMapping("root", "Document"); | |||
addMapping("page-sequence", "Part"); | |||
addMapping("flow", "Sect"); | |||
addMapping("static-content", "Sect"); | |||
// Block-level Formatting Objects | |||
addMapping("block", "P"); | |||
addMapping("block-container", "Div"); | |||
// Inline-level Formatting Objects | |||
addMapping("character", "Span"); | |||
addMapping("external-graphic", "Figure"); | |||
addMapping("instream-foreign-object", "Figure"); | |||
addMapping("inline", "Span"); | |||
addMapping("inline-container", "Div"); | |||
addMapping("page-number", "Quote"); | |||
addMapping("page-number-citation", "Quote"); | |||
addMapping("page-number-citation-last", "Quote"); | |||
// Formatting Objects for Tables | |||
addMapping("table-and-caption", "Div"); | |||
addMapping("table", "Table"); | |||
addMapping("table-caption", "Caption"); | |||
addMapping("table-header", "THead"); | |||
addMapping("table-footer", "TFoot"); | |||
addMapping("table-body", "TBody"); | |||
addMapping("table-row", "TR"); | |||
addMapping("table-cell", new TableCellMapper()); | |||
// Formatting Objects for Lists | |||
addMapping("list-block", "L"); | |||
addMapping("list-item", "LI"); | |||
addMapping("list-item-body", "LBody"); | |||
addMapping("list-item-label", "Lbl"); | |||
// Dynamic Effects: Link and Multi Formatting Objects | |||
addMapping("basic-link", "Link"); | |||
// Out-of-Line Formatting Objects | |||
addMapping("float", "Div"); | |||
addMapping("footnote", "Note"); | |||
addMapping("footnote-body", "Sect"); | |||
addMapping("wrapper", "Span"); | |||
addMapping("marker", "Private"); | |||
} | |||
private static void addStructureType(String structureType) { | |||
STANDARD_STRUCTURE_TYPES.put(structureType, new PDFName(structureType)); | |||
} | |||
private static void addMapping(String fo, String structureType) { | |||
PDFName type = STANDARD_STRUCTURE_TYPES.get(structureType); | |||
assert type != null; | |||
addMapping(fo, new SimpleMapper(type)); | |||
} | |||
private static void addMapping(String fo, Mapper mapper) { | |||
DEFAULT_MAPPINGS.put(fo, mapper); | |||
} | |||
/** | |||
* Maps a Formatting Object to a PDFName representing the associated structure type. | |||
* @param fo the formatting object's local name | |||
* @param role the value of the formatting object's role property | |||
* @param parent the parent of the structure element to be mapped | |||
* @param eventBroadcaster the event broadcaster | |||
* @return the structure type or null if no match could be found | |||
*/ | |||
public static PDFName mapFormattingObject(String fo, String role, | |||
PDFObject parent, EventBroadcaster eventBroadcaster) { | |||
PDFName type = null; | |||
if (role == null) { | |||
type = getDefaultMappingFor(fo, parent); | |||
} else { | |||
type = STANDARD_STRUCTURE_TYPES.get(role); | |||
if (type == null) { | |||
type = getDefaultMappingFor(fo, parent); | |||
PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo, | |||
fo, role, type.toString().substring(1)); | |||
} | |||
} | |||
assert type != null; | |||
return type; | |||
} | |||
/** | |||
* Maps a Formatting Object to a PDFName representing the associated structure type. | |||
* @param fo the formatting object's local name | |||
* @param parent the parent of the structure element to be mapped | |||
* @return the structure type or NonStruct if no match could be found | |||
*/ | |||
private static PDFName getDefaultMappingFor(String fo, PDFObject parent) { | |||
Mapper mapper = DEFAULT_MAPPINGS.get(fo); | |||
if (mapper != null) { | |||
return mapper.getStructureType(parent); | |||
} else { | |||
return NON_STRUCT; | |||
} | |||
} | |||
private interface Mapper { | |||
PDFName getStructureType(PDFObject parent); | |||
} | |||
private static class SimpleMapper implements Mapper { | |||
private PDFName structureType; | |||
public SimpleMapper(PDFName structureType) { | |||
this.structureType = structureType; | |||
} | |||
public PDFName getStructureType(PDFObject parent) { | |||
return structureType; | |||
} | |||
} | |||
private static class TableCellMapper implements Mapper { | |||
public PDFName getStructureType(PDFObject parent) { | |||
PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem(); | |||
//TODO What to do with cells from table-footer? Currently they are mapped on TD. | |||
PDFName type; | |||
if (THEAD.equals(grandParent.getStructureType())) { | |||
type = STANDARD_STRUCTURE_TYPES.get("TH"); | |||
} else { | |||
type = STANDARD_STRUCTURE_TYPES.get("TD"); | |||
} | |||
assert type != null; | |||
return type; | |||
} | |||
} | |||
private FOToPDFRoleMap() { } | |||
} |
@@ -59,12 +59,11 @@ public interface PDFEventProducer extends EventProducer { | |||
* Custom structure type is not standard as per the PDF reference. | |||
* | |||
* @param source the event source | |||
* @param fo the local name of the formatting object having the custom type | |||
* @param type custom structure type | |||
* @param fallback default structure type used as a fallback | |||
* @event.severity WARN | |||
*/ | |||
void nonStandardStructureType(Object source, String fo, String type, String fallback); | |||
void nonStandardStructureType(Object source, String type, String fallback); | |||
/** | |||
* The encryption length must be a multiple of 8 between 40 and 128. |
@@ -132,7 +132,7 @@ class PDFLogicalStructureHandler { | |||
? structureTreeElement.getParentStructElem() | |||
: structureTreeElement; | |||
pageParentTreeArray.add(parent); | |||
String type = parent.getStructureType().toString(); | |||
String type = parent.getStructureType().getName().toString(); | |||
int mcid = pageParentTreeArray.length() - 1; | |||
return new MarkedContentInfo(type, mcid, structureTreeElement); | |||
} |
@@ -21,25 +21,272 @@ package org.apache.fop.render.pdf; | |||
import java.util.LinkedList; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import org.xml.sax.Attributes; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
import org.apache.fop.accessibility.StructureTreeElement; | |||
import org.apache.fop.accessibility.StructureTreeEventHandler; | |||
import org.apache.fop.events.EventBroadcaster; | |||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | |||
import org.apache.fop.fo.pagination.Flow; | |||
import org.apache.fop.pdf.PDFFactory; | |||
import org.apache.fop.pdf.PDFName; | |||
import org.apache.fop.pdf.PDFObject; | |||
import org.apache.fop.pdf.PDFParentTree; | |||
import org.apache.fop.pdf.PDFStructElem; | |||
import org.apache.fop.pdf.PDFStructTreeRoot; | |||
import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope; | |||
import org.apache.fop.pdf.StandardStructureTypes; | |||
import org.apache.fop.pdf.StandardStructureTypes.Grouping; | |||
import org.apache.fop.pdf.StandardStructureTypes.Table; | |||
import org.apache.fop.pdf.StructureHierarchyMember; | |||
import org.apache.fop.pdf.StructureType; | |||
import org.apache.fop.util.XMLUtil; | |||
class PDFStructureTreeBuilder implements StructureTreeEventHandler { | |||
private PDFFactory pdfFactory; | |||
private static final String ROLE = "role"; | |||
private static final Map<String, StructureElementBuilder> BUILDERS | |||
= new java.util.HashMap<String, StructureElementBuilder>(); | |||
private static final StructureElementBuilder DEFAULT_BUILDER | |||
= new DefaultStructureElementBuilder(Grouping.NON_STRUCT); | |||
static { | |||
// Declarations and Pagination and Layout Formatting Objects | |||
StructureElementBuilder regionBuilder = new RegionBuilder(); | |||
addBuilder("root", StandardStructureTypes.Grouping.DOCUMENT); | |||
addBuilder("page-sequence", new PageSequenceBuilder()); | |||
addBuilder("static-content", regionBuilder); | |||
addBuilder("flow", regionBuilder); | |||
// Block-level Formatting Objects | |||
addBuilder("block", StandardStructureTypes.Paragraphlike.P); | |||
addBuilder("block-container", StandardStructureTypes.Grouping.DIV); | |||
// Inline-level Formatting Objects | |||
addBuilder("character", StandardStructureTypes.InlineLevelStructure.SPAN); | |||
addBuilder("external-graphic", new ImageBuilder()); | |||
addBuilder("instream-foreign-object", new ImageBuilder()); | |||
addBuilder("inline", StandardStructureTypes.InlineLevelStructure.SPAN); | |||
addBuilder("inline-container", StandardStructureTypes.Grouping.DIV); | |||
addBuilder("page-number", StandardStructureTypes.InlineLevelStructure.QUOTE); | |||
addBuilder("page-number-citation", StandardStructureTypes.InlineLevelStructure.QUOTE); | |||
addBuilder("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE); | |||
// Formatting Objects for Tables | |||
addBuilder("table-and-caption", StandardStructureTypes.Grouping.DIV); | |||
addBuilder("table", new TableBuilder()); | |||
addBuilder("table-caption", StandardStructureTypes.Grouping.CAPTION); | |||
addBuilder("table-header", StandardStructureTypes.Table.THEAD); | |||
addBuilder("table-footer", new TableFooterBuilder()); | |||
addBuilder("table-body", StandardStructureTypes.Table.TBODY); | |||
addBuilder("table-row", StandardStructureTypes.Table.TR); | |||
addBuilder("table-cell", new TableCellBuilder()); | |||
// Formatting Objects for Lists | |||
addBuilder("list-block", StandardStructureTypes.List.L); | |||
addBuilder("list-item", StandardStructureTypes.List.LI); | |||
addBuilder("list-item-body", StandardStructureTypes.List.LBODY); | |||
addBuilder("list-item-label", StandardStructureTypes.List.LBL); | |||
// Dynamic Effects: Link and Multi Formatting Objects | |||
addBuilder("basic-link", StandardStructureTypes.InlineLevelStructure.LINK); | |||
// Out-of-Line Formatting Objects | |||
addBuilder("float", StandardStructureTypes.Grouping.DIV); | |||
addBuilder("footnote", StandardStructureTypes.InlineLevelStructure.NOTE); | |||
addBuilder("footnote-body", StandardStructureTypes.Grouping.SECT); | |||
addBuilder("wrapper", StandardStructureTypes.InlineLevelStructure.SPAN); | |||
addBuilder("marker", StandardStructureTypes.Grouping.PRIVATE); | |||
addBuilder("#PCDATA", new PlaceholderBuilder()); | |||
} | |||
private static void addBuilder(String fo, StructureType structureType) { | |||
addBuilder(fo, new DefaultStructureElementBuilder(structureType)); | |||
} | |||
private static void addBuilder(String fo, StructureElementBuilder mapper) { | |||
BUILDERS.put(fo, mapper); | |||
} | |||
private interface StructureElementBuilder { | |||
PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, PDFFactory pdfFactory, | |||
EventBroadcaster eventBroadcaster); | |||
} | |||
private PDFLogicalStructureHandler logicalStructureHandler; | |||
private static class DefaultStructureElementBuilder implements StructureElementBuilder { | |||
private final StructureType defaultStructureType; | |||
DefaultStructureElementBuilder(StructureType structureType) { | |||
this.defaultStructureType = structureType; | |||
} | |||
public final PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, | |||
PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { | |||
String role = attributes.getValue(ROLE); | |||
StructureType structureType; | |||
if (role == null) { | |||
structureType = defaultStructureType; | |||
} else { | |||
structureType = StandardStructureTypes.get(role); | |||
if (structureType == null) { | |||
structureType = defaultStructureType; | |||
PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(role, role, | |||
structureType.toString()); | |||
} | |||
} | |||
PDFStructElem structElem = createStructureElement(parent, structureType); | |||
setAttributes(structElem, attributes); | |||
addKidToParent(structElem, parent, attributes); | |||
registerStructureElement(structElem, pdfFactory); | |||
return structElem; | |||
} | |||
protected PDFStructElem createStructureElement(StructureHierarchyMember parent, | |||
StructureType structureType) { | |||
return new PDFStructElem(parent, structureType); | |||
} | |||
protected void setAttributes(PDFStructElem structElem, Attributes attributes) { | |||
} | |||
protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, | |||
Attributes attributes) { | |||
parent.addKid(kid); | |||
} | |||
protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { | |||
pdfFactory.getDocument().registerStructureElement(structureElement); | |||
} | |||
} | |||
private static class PageSequenceBuilder extends DefaultStructureElementBuilder { | |||
PageSequenceBuilder() { | |||
super(StandardStructureTypes.Grouping.PART); | |||
} | |||
@Override | |||
protected PDFStructElem createStructureElement(StructureHierarchyMember parent, | |||
StructureType structureType) { | |||
return new PageSequenceStructElem(parent, structureType); | |||
} | |||
} | |||
private static class RegionBuilder extends DefaultStructureElementBuilder { | |||
RegionBuilder() { | |||
super(StandardStructureTypes.Grouping.SECT); | |||
} | |||
@Override | |||
protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, | |||
Attributes attributes) { | |||
String flowName = attributes.getValue(Flow.FLOW_NAME); | |||
((PageSequenceStructElem) parent).addContent(flowName, kid); | |||
} | |||
} | |||
private static class ImageBuilder extends DefaultStructureElementBuilder { | |||
ImageBuilder() { | |||
super(StandardStructureTypes.Illustration.FIGURE); | |||
} | |||
@Override | |||
protected void setAttributes(PDFStructElem structElem, Attributes attributes) { | |||
String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); | |||
if (altTextNode == null) { | |||
altTextNode = "No alternate text specified"; | |||
} | |||
structElem.put("Alt", altTextNode); | |||
} | |||
} | |||
private static class TableBuilder extends DefaultStructureElementBuilder { | |||
TableBuilder() { | |||
super(StandardStructureTypes.Table.TABLE); | |||
} | |||
@Override | |||
protected PDFStructElem createStructureElement(StructureHierarchyMember parent, | |||
StructureType structureType) { | |||
return new TableStructElem(parent, structureType); | |||
} | |||
} | |||
private static class TableFooterBuilder extends DefaultStructureElementBuilder { | |||
public TableFooterBuilder() { | |||
super(StandardStructureTypes.Table.TFOOT); | |||
} | |||
@Override | |||
protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, | |||
Attributes attributes) { | |||
((TableStructElem) parent).addTableFooter(kid); | |||
} | |||
} | |||
private static class TableCellBuilder extends DefaultStructureElementBuilder { | |||
TableCellBuilder() { | |||
super(StandardStructureTypes.Table.TD); | |||
} | |||
@Override | |||
protected PDFStructElem createStructureElement(StructureHierarchyMember parent, | |||
StructureType structureType) { | |||
PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem(); | |||
//TODO What to do with cells from table-footer? Currently they are mapped on TD. | |||
if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) { | |||
structureType = StandardStructureTypes.Table.TH; | |||
} else { | |||
structureType = StandardStructureTypes.Table.TD; | |||
} | |||
return super.createStructureElement(parent, structureType); | |||
} | |||
@Override | |||
protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { | |||
if (structureElement.getStructureType() == Table.TH) { | |||
pdfFactory.getDocument().registerStructureElement(structureElement, Scope.COLUMN); | |||
} else { | |||
pdfFactory.getDocument().registerStructureElement(structureElement); | |||
} | |||
} | |||
@Override | |||
protected void setAttributes(PDFStructElem structElem, Attributes attributes) { | |||
String columnSpan = attributes.getValue("number-columns-spanned"); | |||
if (columnSpan != null) { | |||
structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan)); | |||
} | |||
String rowSpan = attributes.getValue("number-rows-spanned"); | |||
if (rowSpan != null) { | |||
structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan)); | |||
} | |||
} | |||
} | |||
private static class PlaceholderBuilder implements StructureElementBuilder { | |||
public PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, | |||
PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { | |||
PDFStructElem elem = new PDFStructElem.Placeholder(parent); | |||
parent.addKid(elem); | |||
return elem; | |||
} | |||
} | |||
private PDFFactory pdfFactory; | |||
private EventBroadcaster eventBroadcaster; | |||
@@ -51,97 +298,65 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { | |||
this.pdfFactory = pdfFactory; | |||
} | |||
void setEventBroadcaster(EventBroadcaster eventBroadcaster) { | |||
this.eventBroadcaster = eventBroadcaster; | |||
} | |||
void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) { | |||
this.logicalStructureHandler = logicalStructureHandler; | |||
createRootStructureElement(); | |||
createRootStructureElement(logicalStructureHandler); | |||
} | |||
private void createRootStructureElement() { | |||
private void createRootStructureElement(PDFLogicalStructureHandler logicalStructureHandler) { | |||
assert rootStructureElement == null; | |||
PDFParentTree parentTree = logicalStructureHandler.getParentTree(); | |||
PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree); | |||
rootStructureElement = createStructureElement("root", structTreeRoot, null); | |||
structTreeRoot.addKid(rootStructureElement); | |||
rootStructureElement = createStructureElement("root", structTreeRoot, | |||
new AttributesImpl(), pdfFactory, eventBroadcaster); | |||
} | |||
void setEventBroadcaster(EventBroadcaster eventBroadcaster) { | |||
this.eventBroadcaster = eventBroadcaster; | |||
} | |||
private static PDFStructElem createStructureElement(String name, StructureHierarchyMember parent, | |||
Attributes attributes, PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { | |||
StructureElementBuilder builder = BUILDERS.get(name); | |||
if (builder == null) { | |||
// TODO is a fallback really necessary? | |||
builder = DEFAULT_BUILDER; | |||
} | |||
return builder.build(parent, attributes, pdfFactory, eventBroadcaster); | |||
} | |||
public void startPageSequence(Locale language, String role) { | |||
ancestors = new LinkedList<PDFStructElem>(); | |||
PDFStructElem structElem = createStructureElement("page-sequence", rootStructureElement, role); | |||
AttributesImpl attributes = new AttributesImpl(); | |||
attributes.addAttribute("", ROLE, ROLE, XMLUtil.CDATA, role); | |||
PDFStructElem structElem = createStructureElement("page-sequence", | |||
rootStructureElement, attributes, pdfFactory, eventBroadcaster); | |||
if (language != null) { | |||
structElem.setLanguage(language); | |||
} | |||
rootStructureElement.addKid(structElem); | |||
ancestors.add(structElem); | |||
} | |||
private PDFStructElem createStructureElement(String name, PDFObject parent, String role) { | |||
PDFName structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster); | |||
return pdfFactory.getDocument().makeStructureElement(structureType, parent); | |||
} | |||
public void endPageSequence() { | |||
} | |||
public StructureTreeElement startNode(String name, Attributes attributes) { | |||
PDFStructElem parent = ancestors.getFirst(); | |||
String role = attributes.getValue("role"); | |||
PDFStructElem structElem = createStructureElement(name, parent, role); | |||
setSpanAttributes(structElem, attributes); | |||
parent.addKid(structElem); | |||
PDFStructElem structElem = createStructureElement(name, parent, attributes, | |||
pdfFactory, eventBroadcaster); | |||
ancestors.addFirst(structElem); | |||
return structElem; | |||
} | |||
private void setSpanAttributes(PDFStructElem structElem, Attributes attributes) { | |||
String columnSpan = attributes.getValue("number-columns-spanned"); | |||
if (columnSpan != null) { | |||
structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan)); | |||
} | |||
String rowSpan = attributes.getValue("number-rows-spanned"); | |||
if (rowSpan != null) { | |||
structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan)); | |||
} | |||
} | |||
public void endNode(String name) { | |||
removeFirstAncestor(); | |||
} | |||
private void removeFirstAncestor() { | |||
ancestors.removeFirst(); | |||
} | |||
public StructureTreeElement startImageNode(String name, Attributes attributes) { | |||
PDFStructElem parent = ancestors.getFirst(); | |||
String role = attributes.getValue("role"); | |||
PDFStructElem structElem = createStructureElement(name, parent, role); | |||
parent.addKid(structElem); | |||
String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); | |||
if (altTextNode != null) { | |||
structElem.put("Alt", altTextNode); | |||
} else { | |||
structElem.put("Alt", "No alternate text specified"); | |||
} | |||
ancestors.addFirst(structElem); | |||
return structElem; | |||
return startNode(name, attributes); | |||
} | |||
public StructureTreeElement startReferencedNode(String name, Attributes attributes) { | |||
PDFStructElem parent = ancestors.getFirst(); | |||
String role = attributes.getValue("role"); | |||
PDFStructElem structElem; | |||
if ("#PCDATA".equals(name)) { | |||
structElem = new PDFStructElem.Placeholder(parent, name); | |||
} else { | |||
structElem = createStructureElement(name, parent, role); | |||
} | |||
parent.addKid(structElem); | |||
ancestors.addFirst(structElem); | |||
return structElem; | |||
return startNode(name, attributes); | |||
} | |||
} |
@@ -0,0 +1,79 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.pdf; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.fop.pdf.PDFArray; | |||
import org.apache.fop.pdf.PDFObject; | |||
import org.apache.fop.pdf.PDFStructElem; | |||
import org.apache.fop.pdf.StructureType; | |||
class PageSequenceStructElem extends PDFStructElem { | |||
private List<PDFStructElem> regionBefores = new ArrayList<PDFStructElem>(); | |||
private List<PDFStructElem> regionAfters = new ArrayList<PDFStructElem>(); | |||
private List<PDFStructElem> regionStarts = new ArrayList<PDFStructElem>(); | |||
private List<PDFStructElem> regionEnds = new ArrayList<PDFStructElem>(); | |||
PageSequenceStructElem(PDFObject parent, StructureType structureType) { | |||
super(parent, structureType); | |||
} | |||
void addContent(String flowName, PDFStructElem content) { | |||
if (flowName.equals("xsl-region-before")) { | |||
regionBefores.add(content); | |||
} else if (flowName.equals("xsl-region-after")) { | |||
regionAfters.add(content); | |||
} else if (flowName.equals("xsl-region-start")) { | |||
regionStarts.add(content); | |||
} else if (flowName.equals("xsl-region-end")) { | |||
regionEnds.add(content); | |||
} else { | |||
addKid(content); | |||
} | |||
} | |||
@Override | |||
protected boolean attachKids() { | |||
assert !kids.isEmpty(); | |||
PDFArray k = new PDFArray(); | |||
addRegions(k, regionBefores); | |||
addRegions(k, regionStarts); | |||
addRegions(k, kids); | |||
addRegions(k, regionEnds); | |||
addRegions(k, regionAfters); | |||
put("K", k); | |||
return true; | |||
} | |||
private void addRegions(PDFArray k, List<? extends PDFObject> regions) { | |||
if (!regions.isEmpty()) { | |||
for (PDFObject kid : regions) { | |||
k.add(kid); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.pdf; | |||
import org.apache.fop.pdf.PDFObject; | |||
import org.apache.fop.pdf.PDFStructElem; | |||
import org.apache.fop.pdf.StructureType; | |||
class TableStructElem extends PDFStructElem { | |||
private PDFStructElem tableFooter; | |||
public TableStructElem(PDFObject parent, StructureType structureType) { | |||
super(parent, structureType); | |||
} | |||
void addTableFooter(PDFStructElem footer) { | |||
assert tableFooter == null; | |||
tableFooter = footer; | |||
} | |||
@Override | |||
protected boolean attachKids() { | |||
assert !kids.isEmpty(); | |||
if (tableFooter != null) { | |||
kids.add(tableFooter); | |||
} | |||
return super.attachKids(); | |||
} | |||
} |
@@ -38,7 +38,7 @@ import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | |||
import org.apache.fop.fonts.Base14Font; | |||
import org.apache.fop.fonts.CIDFontType; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CIDSet; | |||
import org.apache.fop.fonts.CMapSegment; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
@@ -457,15 +457,17 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
// TODO /FontInfo | |||
gen.write("/CIDCount "); | |||
CIDSubset cidSubset = font.getCIDSubset(); | |||
int subsetSize = cidSubset.getSubsetSize(); | |||
gen.write(subsetSize); | |||
CIDSet cidSet = font.getCIDSet(); | |||
int numberOfGlyphs = cidSet.getNumberOfGlyphs(); | |||
gen.write(numberOfGlyphs); | |||
gen.writeln(" def"); | |||
gen.writeln("/GDBytes 2 def"); // TODO always 2? | |||
gen.writeln("/CIDMap [<"); | |||
int colCount = 0; | |||
int lineCount = 1; | |||
for (int cid = 0; cid < subsetSize; cid++) { | |||
int nextBitSet = 0; | |||
int previousBitSet = 0; | |||
for (int cid = 0; cid < numberOfGlyphs; cid++) { | |||
if (colCount++ == 20) { | |||
gen.newLine(); | |||
colCount = 1; | |||
@@ -478,7 +480,23 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
gid = HexEncoder.encode(cid, 4); | |||
} else { | |||
gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); | |||
previousBitSet = nextBitSet; | |||
nextBitSet = cidSet.getGlyphIndices().nextSetBit(nextBitSet); | |||
while (previousBitSet++ < nextBitSet) { | |||
// if there are gaps in the indices we pad them with zeros | |||
gen.write("0000"); | |||
cid++; | |||
if (colCount++ == 20) { | |||
gen.newLine(); | |||
colCount = 1; | |||
if (lineCount++ == 800) { | |||
gen.writeln("> <"); | |||
lineCount = 1; | |||
} | |||
} | |||
} | |||
gid = HexEncoder.encode(nextBitSet, 4); | |||
nextBitSet++; | |||
} | |||
gen.write(gid); | |||
} |
@@ -62,6 +62,62 @@ | |||
documents. Example: the fix of marks layering will be such a case when it's done. | |||
--> | |||
<release version="FOP Trunk" date="TBD"> | |||
<action context="Fonts" dev="MH" type="add" fixes-bug="53868" importance="low" due-to="Luis Bernardo"> | |||
Full font embedding in PDF | |||
</action> | |||
<action context="Renderers" dev="PH" type="add" fixes-bug="53865" importance="low"> | |||
Added configuration for RowPerStrip configuration in the Tiff renderer. | |||
RowsPerStrip can be configured to 1 or to the total # of rows. | |||
See docs for fop.xconf configuration details. | |||
</action> | |||
<action context="Layout" dev="VH" type="fix" fixes-bug="53598" due-to="Robert Meyer"> | |||
Always set the breakClass field to a legal value in BreakElement, so as to avoid | |||
IllegalArgumentExceptions in other parts of the code. | |||
</action> | |||
<action context="Layout" dev="VH" type="fix" fixes-bug="45715" due-to="Luis Bernardo"> | |||
Restored support for break-before on fo:table. | |||
</action> | |||
<action context="Layout" dev="VH" type="fix" fixes-bug="53827"> | |||
When an fo:block has a non-zero value for its text-indent property and is broken over two | |||
pages of different widths, then the first line on the second page is missing one word and | |||
appears indented. | |||
</action> | |||
<action context="Renderers" dev="MH" type="fix" fixes-bug="53790"> | |||
Prevented the TIFF configurator from overriding the Bitmap configurator unless CCITT | |||
compression is enabled. | |||
</action> | |||
<action context="Renderers" dev="MH" type="fix" fixes-bug="53786"> | |||
Removed the Attribute Qualifier on TLEs as they aren't used. | |||
</action> | |||
<action context="Renderers" dev="MH" type="fix" fixes-bug="48954" due-to="PH"> | |||
Support for character encoding of TLEs in AFP output | |||
</action> | |||
<action context="Renderers" dev="VH" type="fix" fixes-bug="53778"> | |||
When PDF accessibility is enabled, the contents for the different regions must appear in the | |||
proper order in the structure tree. | |||
</action> | |||
<action context="Renderers" dev="MH" type="fix" fixes-bug="53766" due-to="Robert Meyer"> | |||
Remove StandardEncoding as the encoding type from fonts used in the PDF renderer | |||
</action> | |||
<action context="Fonts" dev="MH" type="fix" fixes-bug="53685"> | |||
Cached AFP charactersets have more unique keys preventing the two characters with | |||
but different binaries conflicting. | |||
</action> | |||
<action context="Fonts" dev="MH" type="fix" fixes-bug="53657" due-to="Robert Meyer"> | |||
AFP fonts default to the nominal character increment to font metrics when glyph info | |||
is missing from the characterset. | |||
</action> | |||
<action context="Layout" dev="VH" type="fix" fixes-bug="53688"> | |||
Wrong page number reported when a column overflows the region-body in a multi-column | |||
document. | |||
</action> | |||
<action context="Renderers" dev="VH" type="add" fixes-bug="53639"> | |||
When PDF accessibility is enabled, the Scope attribute must be present in the structure tree | |||
for table header elements. | |||
</action> | |||
<action context="Fonts" dev="MH" type="add" fixes-bug="53600" due-to="Robert Meyer"> | |||
Added an event if a glyph and its metric information does not exist in the character set | |||
</action> | |||
<action context="Renderers" dev="VH" type="add" fixes-bug="53596"> | |||
When PDF accessibility is enabled, the structure tree must contain information about the | |||
number of columns or rows spanned by a table cell. |
@@ -0,0 +1,115 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
--> | |||
<!-- $Id$ --> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="page" | |||
page-height="80pt" page-width="530pt" margin="10pt" margin-bottom="0"> | |||
<fo:region-body margin-bottom="20pt" column-count="5" column-gap="10pt"/> | |||
<fo:region-after extent="15pt"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="page" font-size="8pt" line-height="10pt"> | |||
<fo:static-content flow-name="xsl-region-after"> | |||
<fo:block text-align="center"><fo:page-number/></fo:block> | |||
</fo:static-content> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block>Page 1 Column 1 Line 1</fo:block> | |||
<fo:block>Page 1 Column 1 Line 2</fo:block> | |||
<fo:block>Page 1 Column 1 Line 3</fo:block> | |||
<fo:block>Page 1 Column 1 Line 4</fo:block> | |||
<fo:block>Page 1 Column 1 Line 5</fo:block> | |||
<fo:block>Page 1 Column 2 Line 1</fo:block> | |||
<fo:block>Page 1 Column 2 Line 2</fo:block> | |||
<fo:block>Page 1 Column 2 Line 3</fo:block> | |||
<fo:block>Page 1 Column 2 Line 4</fo:block> | |||
<fo:block>Page 1 Column 2 Line 5</fo:block> | |||
<fo:block>Page 1 Column 3 Line 1</fo:block> | |||
<fo:block>Page 1 Column 3 Line 2</fo:block> | |||
<fo:block>Page 1 Column 3 Line 3</fo:block> | |||
<fo:block>Page 1 Column 3 Line 4</fo:block> | |||
<fo:block>Page 1 Column 3 Line 5</fo:block> | |||
<fo:block>Page 1 Column 4 Line 1</fo:block> | |||
<fo:block>Page 1 Column 4 Line 2</fo:block> | |||
<fo:block>Page 1 Column 4 Line 3</fo:block> | |||
<fo:block>Page 1 Column 4 Line 4</fo:block> | |||
<fo:block>Page 1 Column 4 Line 5</fo:block> | |||
<fo:block-container height="55pt" background-color="#F0F0F0"> | |||
<fo:block>Page 1 Column 5 Line 1</fo:block> | |||
<fo:block>Page 1 Column 5 Line 2</fo:block> | |||
<fo:block>Page 1 Column 5 Line 3</fo:block> | |||
<fo:block>Page 1 Column 5 Line 4</fo:block> | |||
<fo:block>Page 1 Column 5 Line 5</fo:block> | |||
</fo:block-container> | |||
<fo:block break-before="page">Page 2 Column 1 Line 1</fo:block> | |||
<fo:block>Page 2 Column 1 Line 2</fo:block> | |||
<fo:block>Page 2 Column 1 Line 3</fo:block> | |||
<fo:block>Page 2 Column 1 Line 4</fo:block> | |||
<fo:block>Page 2 Column 1 Line 5</fo:block> | |||
<fo:block>Page 2 Column 2 Line 1</fo:block> | |||
<fo:block>Page 2 Column 2 Line 2</fo:block> | |||
<fo:block>Page 2 Column 2 Line 3</fo:block> | |||
<fo:block>Page 2 Column 2 Line 4</fo:block> | |||
<fo:block>Page 2 Column 2 Line 5</fo:block> | |||
<fo:block>Page 2 Column 3 Line 1</fo:block> | |||
<fo:block>Page 2 Column 3 Line 2</fo:block> | |||
<fo:block>Page 2 Column 3 Line 3</fo:block> | |||
<fo:block>Page 2 Column 3 Line 4</fo:block> | |||
<fo:block>Page 2 Column 3 Line 5</fo:block> | |||
<fo:block>Page 2 Column 4 Line 1</fo:block> | |||
<fo:block>Page 2 Column 4 Line 2</fo:block> | |||
<fo:block>Page 2 Column 4 Line 3</fo:block> | |||
<fo:block>Page 2 Column 4 Line 4</fo:block> | |||
<fo:block>Page 2 Column 4 Line 5</fo:block> | |||
<fo:block>Page 2 Column 5 Line 1</fo:block> | |||
<fo:block>Page 2 Column 5 Line 2</fo:block> | |||
<fo:block>Page 2 Column 5 Line 3</fo:block> | |||
<fo:block>Page 2 Column 5 Line 4</fo:block> | |||
<fo:block>Page 2 Column 5 Line 5</fo:block> | |||
<fo:block>Page 3 Column 1 Line 1</fo:block> | |||
<fo:block>Page 3 Column 1 Line 2</fo:block> | |||
<fo:block>Page 3 Column 1 Line 3</fo:block> | |||
<fo:block>Page 3 Column 1 Line 4</fo:block> | |||
<fo:block>Page 3 Column 1 Line 5</fo:block> | |||
<fo:block>Page 3 Column 2 Line 1</fo:block> | |||
<fo:block>Page 3 Column 2 Line 2</fo:block> | |||
<fo:block>Page 3 Column 2 Line 3</fo:block> | |||
<fo:block>Page 3 Column 2 Line 4</fo:block> | |||
<fo:block>Page 3 Column 2 Line 5</fo:block> | |||
<fo:block>Page 3 Column 3 Line 1</fo:block> | |||
<fo:block>Page 3 Column 3 Line 2</fo:block> | |||
<fo:block>Page 3 Column 3 Line 3</fo:block> | |||
<fo:block>Page 3 Column 3 Line 4</fo:block> | |||
<fo:block>Page 3 Column 3 Line 5</fo:block> | |||
<fo:block>Page 3 Column 4 Line 1</fo:block> | |||
<fo:block>Page 3 Column 4 Line 2</fo:block> | |||
<fo:block>Page 3 Column 4 Line 3</fo:block> | |||
<fo:block>Page 3 Column 4 Line 4</fo:block> | |||
<fo:block>Page 3 Column 4 Line 5</fo:block> | |||
<fo:block>Page 3 Column 5 Line 1</fo:block> | |||
<fo:block>Page 3 Column 5 Line 2</fo:block> | |||
<fo:block>Page 3 Column 5 Line 3</fo:block> | |||
<fo:block>Page 3 Column 5 Line 4</fo:block> | |||
<fo:block>Page 3 Column 5 Line 5</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |
@@ -19,8 +19,6 @@ | |||
package org.apache.fop.accessibility.fo; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
@@ -34,7 +32,6 @@ import javax.xml.transform.TransformerFactoryConfigurationError; | |||
import javax.xml.transform.dom.DOMResult; | |||
import javax.xml.transform.sax.SAXTransformerFactory; | |||
import javax.xml.transform.sax.TransformerHandler; | |||
import javax.xml.transform.stream.StreamResult; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.custommonkey.xmlunit.Diff; | |||
@@ -57,9 +54,17 @@ import org.apache.fop.fotreetest.DummyFOEventHandler; | |||
public class FO2StructureTreeConverterTestCase { | |||
private interface FOLoader { | |||
private static class FOLoader { | |||
InputStream getFoInputStream(); | |||
private final String resourceName; | |||
FOLoader(String resourceName) { | |||
this.resourceName = resourceName; | |||
} | |||
public InputStream getFoInputStream() { | |||
return getResource(resourceName); | |||
} | |||
} | |||
private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence"; | |||
@@ -68,62 +73,30 @@ public class FO2StructureTreeConverterTestCase { | |||
@Test | |||
public void testCompleteDocument() throws Exception { | |||
foLoader = new FOLoader() { | |||
public InputStream getFoInputStream() { | |||
return getResource("/org/apache/fop/fo/complete_document.fo"); | |||
} | |||
}; | |||
testConverter(); | |||
testConverter("/org/apache/fop/fo/complete_document.fo"); | |||
} | |||
@Test | |||
public void testTableFooters() throws Exception { | |||
foLoader = new FOLoader() { | |||
public InputStream getFoInputStream() { | |||
return getResource("table-footers.fo"); | |||
} | |||
}; | |||
testConverter(); | |||
} | |||
@Test | |||
public void testCompleteContentWrappedInTableFooter() throws Exception { | |||
Source xslt = new StreamSource(getResource("wrapCompleteDocumentInTableFooter.xsl")); | |||
Transformer transformer = createTransformer(xslt); | |||
InputStream originalFO = getResource("/org/apache/fop/fo/complete_document.fo"); | |||
ByteArrayOutputStream transformedFoOutput = new ByteArrayOutputStream(); | |||
transformer.transform(new StreamSource(originalFO), new StreamResult(transformedFoOutput)); | |||
final byte[] transformedFoOutputBytes = transformedFoOutput.toByteArray(); | |||
foLoader = new FOLoader() { | |||
public InputStream getFoInputStream() { | |||
return new ByteArrayInputStream(transformedFoOutputBytes); | |||
} | |||
}; | |||
testConverter(); | |||
testConverter("table-footers.fo"); | |||
} | |||
@Test | |||
public void testArtifact() throws Exception { | |||
foLoader = new FOLoader() { | |||
public InputStream getFoInputStream() { | |||
return getResource("artifact.fo"); | |||
} | |||
}; | |||
testConverter(); | |||
testConverter("artifact.fo"); | |||
} | |||
private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError, | |||
TransformerConfigurationException { | |||
TransformerFactory transformerFactory = TransformerFactory.newInstance(); | |||
return transformerFactory.newTransformer(xslt); | |||
@Test | |||
public void testSideRegions() throws Exception { | |||
testConverter("/org/apache/fop/fo/pagination/side-regions.fo"); | |||
} | |||
private static InputStream getResource(String name) { | |||
return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name); | |||
} | |||
private void testConverter() throws Exception { | |||
private void testConverter(String foResourceName) throws Exception { | |||
foLoader = new FOLoader(foResourceName); | |||
DOMResult expectedStructureTree = loadExpectedStructureTree(); | |||
DOMResult actualStructureTree = buildActualStructureTree(); | |||
final Diff diff = createDiff(expectedStructureTree, actualStructureTree); |
@@ -50,6 +50,25 @@ | |||
<xsl:call-template name="copy"/> | |||
</xsl:template> | |||
<xsl:template match="fo:static-content/@flow-name|fo:flow/@flow-name"> | |||
<xsl:choose> | |||
<xsl:when test=". = 'xsl-region-body' or | |||
. = 'xsl-region-before' or | |||
. = 'xsl-region-after' or | |||
. = 'xsl-region-start' or | |||
. = 'xsl-region-end' or | |||
. = 'xsl-before-float-separator' or | |||
. = 'xsl-footnote-separator'"> | |||
<xsl:copy/> | |||
</xsl:when> | |||
<xsl:otherwise> | |||
<xsl:attribute name="{local-name()}"> | |||
<xsl:value-of select="concat('xsl-', local-name(//*[@region-name = current()]))"/> | |||
</xsl:attribute> | |||
</xsl:otherwise> | |||
</xsl:choose> | |||
</xsl:template> | |||
<!-- Block-level Formatting Objects --> | |||
<xsl:template match="fo:block|fo:block-container"> | |||
<xsl:call-template name="copy"/> | |||
@@ -73,15 +92,7 @@ | |||
<xsl:call-template name="copy"/> | |||
</xsl:template> | |||
<xsl:template match="fo:table"> | |||
<xsl:copy> | |||
<xsl:apply-templates select="@*"/> | |||
<xsl:apply-templates select="*[name() != 'fo:table-footer']"/> | |||
<xsl:apply-templates select="fo:table-footer"/> | |||
</xsl:copy> | |||
</xsl:template> | |||
<xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> | |||
<xsl:template match="fo:table|fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> | |||
<xsl:call-template name="copy"/> | |||
</xsl:template> | |||
@@ -1,66 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
--> | |||
<!-- $Id$ --> | |||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | |||
xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<xsl:template match="@*|node()" name="copy"> | |||
<xsl:copy> | |||
<xsl:apply-templates select="@*|node()"/> | |||
</xsl:copy> | |||
</xsl:template> | |||
<xsl:template match="/"> | |||
<fo:root> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="page" | |||
page-height="500pt" page-width="300pt" margin="20pt"> | |||
<fo:region-body margin-top="20pt"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<xsl:apply-templates select="//fo:page-sequence"/> | |||
</fo:root> | |||
</xsl:template> | |||
<xsl:template match="fo:page-sequence"> | |||
<fo:page-sequence master-reference="page"> | |||
<xsl:apply-templates select="fo:flow"/> | |||
</fo:page-sequence> | |||
</xsl:template> | |||
<xsl:template match="fo:flow"> | |||
<xsl:copy> | |||
<xsl:apply-templates select="@*[not(starts-with(name(), 'space-before'))]"/> | |||
<fo:table width="100%" table-layout="fixed"> | |||
<fo:table-footer> | |||
<fo:table-cell background-color="#F0F0F0"> | |||
<xsl:apply-templates select="@*[starts-with(name(), 'space-before')]"/> | |||
<xsl:apply-templates select="*"/> | |||
</fo:table-cell> | |||
</fo:table-footer> | |||
<fo:table-body> | |||
<fo:table-cell> | |||
<fo:block>The content below is in the table footer.</fo:block> | |||
</fo:table-cell> | |||
</fo:table-body> | |||
</fo:table> | |||
</xsl:copy> | |||
</xsl:template> | |||
</xsl:stylesheet> |
@@ -0,0 +1,84 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.apache.fop.afp.util; | |||
import java.io.IOException; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
public class AFPResourceAccessorTestCase { | |||
private InternalResourceResolver nullBaseResourceResolver; | |||
private InternalResourceResolver absoluteBaseResourceResolver; | |||
private InternalResourceResolver relativeBaseResourceResolver; | |||
private final URI absoluteBaseURI = URI.create("this:///purely.for.testing"); | |||
private final URI relativeBaseURI = URI.create("./this.is.purely.for.testing"); | |||
private AFPResourceAccessor nullBaseURISut; | |||
private AFPResourceAccessor absoluteBaseURISut; | |||
private AFPResourceAccessor relativeBaseURISut; | |||
@Before | |||
public void setUp() { | |||
nullBaseResourceResolver = mock(InternalResourceResolver.class); | |||
absoluteBaseResourceResolver = mock(InternalResourceResolver.class); | |||
relativeBaseResourceResolver = mock(InternalResourceResolver.class); | |||
nullBaseURISut = new AFPResourceAccessor(nullBaseResourceResolver); | |||
absoluteBaseURISut = new AFPResourceAccessor(absoluteBaseResourceResolver, | |||
absoluteBaseURI.toASCIIString()); | |||
relativeBaseURISut = new AFPResourceAccessor(relativeBaseResourceResolver, | |||
relativeBaseURI.toASCIIString()); | |||
} | |||
@Test | |||
public void testCreateInputStream() throws IOException, URISyntaxException { | |||
URI testURI = URI.create("test"); | |||
nullBaseURISut.createInputStream(testURI); | |||
verify(nullBaseResourceResolver).getResource(testURI); | |||
absoluteBaseURISut.createInputStream(testURI); | |||
verify(absoluteBaseResourceResolver).getResource(getActualURI(absoluteBaseURI, testURI)); | |||
relativeBaseURISut.createInputStream(testURI); | |||
verify(relativeBaseResourceResolver).getResource(getActualURI(relativeBaseURI, testURI)); | |||
} | |||
private URI getActualURI(URI baseURI, URI testURI) throws URISyntaxException { | |||
return InternalResourceResolver.getBaseURI(baseURI.toASCIIString()).resolve(testURI); | |||
} | |||
@Test | |||
public void testResolveURI() throws URISyntaxException { | |||
String testURI = "anotherTestURI"; | |||
assertEquals(URI.create("./" + testURI), nullBaseURISut.resolveURI(testURI)); | |||
assertEquals(getActualURI(absoluteBaseURI, URI.create(testURI)), | |||
absoluteBaseURISut.resolveURI(testURI)); | |||
assertEquals(getActualURI(relativeBaseURI, URI.create(testURI)), | |||
relativeBaseURISut.resolveURI(testURI)); | |||
} | |||
} |
@@ -20,7 +20,7 @@ | |||
package org.apache.fop.apps; | |||
import static org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererOption.COMPRESSION; | |||
import static org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererOption.SINGLE_STRIP; | |||
public class TIFFRendererConfBuilder extends BitmapRendererConfBuilder { | |||
public TIFFRendererConfBuilder() { | |||
@@ -31,4 +31,9 @@ public class TIFFRendererConfBuilder extends BitmapRendererConfBuilder { | |||
createTextElement(COMPRESSION, mode); | |||
return this; | |||
} | |||
public TIFFRendererConfBuilder setSingleStrip(boolean single) { | |||
createTextElement(SINGLE_STRIP, String.valueOf(single)); | |||
return this; | |||
} | |||
} |
@@ -19,6 +19,9 @@ | |||
package org.apache.fop.events; | |||
import java.util.Map; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.fail; | |||
/** | |||
@@ -28,10 +31,13 @@ class EventChecker implements EventListener { | |||
private final String expectedEventID; | |||
private final Map<String, Object> expectedParams; | |||
private boolean eventReceived; | |||
EventChecker(String expectedEventID) { | |||
EventChecker(String expectedEventID, Map<String, Object> expectedParams) { | |||
this.expectedEventID = expectedEventID; | |||
this.expectedParams = expectedParams; | |||
} | |||
public void processEvent(Event event) { | |||
@@ -39,6 +45,9 @@ class EventChecker implements EventListener { | |||
String id = event.getEventID(); | |||
if (id.equals(expectedEventID)) { | |||
eventReceived = true; | |||
for (Map.Entry<String, Object> param : expectedParams.entrySet()) { | |||
assertEquals(event.getParam(param.getKey()), param.getValue()); | |||
} | |||
} | |||
} | |||
@@ -22,6 +22,9 @@ package org.apache.fop.events; | |||
import java.io.File; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.xml.transform.Result; | |||
import javax.xml.transform.Source; | |||
@@ -62,9 +65,10 @@ public class EventProcessingTestCase { | |||
CONFIG_BASE_DIR = base.resolve("test/config/"); | |||
} | |||
public void doTest(InputStream inStream, URI fopConf, String expectedEventID, String mimeType) | |||
throws Exception { | |||
EventChecker eventChecker = new EventChecker(expectedEventID); | |||
public void doTest(InputStream inStream, URI fopConf, String expectedEventID, String mimeType, | |||
Map<String, Object> expectedParams) throws Exception { | |||
EventChecker eventChecker = new EventChecker(expectedEventID, expectedParams); | |||
FopFactory fopFactory; | |||
if (fopConf != null) { | |||
fopFactory = FopFactory.newInstance(new File(fopConf)); | |||
@@ -81,6 +85,19 @@ public class EventProcessingTestCase { | |||
Result res = new SAXResult(fop.getDefaultHandler()); | |||
transformer.transform(src, res); | |||
eventChecker.end(); | |||
} | |||
public void doTest(InputStream inStream, URI fopConf, String expectedEventID, String mimeType) | |||
throws Exception { | |||
Map<String, Object> noParams = Collections.emptyMap(); | |||
doTest(inStream, fopConf, expectedEventID, mimeType, noParams); | |||
} | |||
public void doTest(String filename, String expectedEventID, Map<String, Object> expectedParams) | |||
throws Exception { | |||
doTest(BASE_DIR.resolve(filename).toURL().openStream(), null, expectedEventID, | |||
MimeConstants.MIME_PDF, expectedParams); | |||
} | |||
public void doTest(String filename, String expectedEventID) throws Exception { | |||
@@ -133,4 +150,12 @@ public class EventProcessingTestCase { | |||
public void testViewportBPDOverflow() throws Exception { | |||
doTest("viewport-overflow.fo", BlockLevelEventProducer.class.getName() + ".viewportBPDOverflow"); | |||
} | |||
@Test | |||
public void testPageOverflow() throws Exception { | |||
Map<String, Object> params = new HashMap<String, Object>(); | |||
params.put("page", "1"); | |||
doTest("region-body_overflow.fo", BlockLevelEventProducer.class.getName() + ".regionOverflow", | |||
params); | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.fo.pagination; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.fo.FODocumentParser; | |||
import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; | |||
import org.apache.fop.fo.FOEventHandler; | |||
public class LayoutMasterSetTestCase { | |||
private static class FlowMappingTester extends FOEventHandler { | |||
private static final String[][] FLOW_MAPPINGS = { | |||
{"first-page-before", "xsl-region-before"}, | |||
{"first-page-after", "xsl-region-after"}, | |||
{"first-page-start", "xsl-region-start"}, | |||
{"first-page-end", "xsl-region-end"}, | |||
{"odd-page-before", "xsl-region-before"}, | |||
{"odd-page-after", "xsl-region-after"}, | |||
{"odd-page-start", "xsl-region-start"}, | |||
{"odd-page-end", "xsl-region-end"}, | |||
{"odd-page-before", "xsl-region-before"}, | |||
{"odd-page-after", "xsl-region-after"}, | |||
{"odd-page-start", "xsl-region-start"}, | |||
{"odd-page-end", "xsl-region-end"}, | |||
{"blank-page-before", "xsl-region-before"}, | |||
{"blank-page-after", "xsl-region-after"}, | |||
{"blank-page-start", "xsl-region-start"}, | |||
{"blank-page-end", "xsl-region-end"}, | |||
{"last-page-before", "xsl-region-before"}, | |||
{"last-page-after", "xsl-region-after"}, | |||
{"last-page-start", "xsl-region-start"}, | |||
{"last-page-end", "xsl-region-end"}, | |||
{"xsl-footnote-separator", "xsl-footnote-separator"} | |||
}; | |||
FlowMappingTester(FOUserAgent userAgent) { | |||
super(userAgent); | |||
} | |||
@Override | |||
public void startPageSequence(PageSequence pageSeq) { | |||
super.startPageSequence(pageSeq); | |||
LayoutMasterSet layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet(); | |||
for (String[] mapping : FLOW_MAPPINGS) { | |||
assertEquals(mapping[1], layoutMasterSet.getDefaultRegionNameFor(mapping[0])); | |||
} | |||
} | |||
} | |||
/** | |||
* Tests the {@link LayoutMasterSet#getDefaultRegionNameFor(String)} method. | |||
*/ | |||
@Test | |||
public void testFlowMapping() throws Exception { | |||
FODocumentParser foDocumentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { | |||
public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { | |||
return new FlowMappingTester(foUserAgent); | |||
} | |||
}); | |||
foDocumentParser.parse(getClass().getResourceAsStream("side-regions.fo")); | |||
} | |||
} |
@@ -0,0 +1,181 @@ | |||
<?xml version="1.0" standalone="no"?> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="first-page" | |||
page-height="100pt" page-width="150pt"> | |||
<fo:region-body margin="12pt" display-align="center" background-color="#FFF0F0"/> | |||
<fo:region-before region-name="first-page-before" extent="10pt" precedence="true" | |||
display-align="after"/> | |||
<fo:region-after region-name="first-page-after" extent="10pt" precedence="true"/> | |||
<fo:region-start region-name="first-page-start" extent="10pt" reference-orientation="90" | |||
display-align="after"/> | |||
<fo:region-end region-name="first-page-end" extent="10pt" reference-orientation="-90" | |||
display-align="after"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master master-name="odd-page" | |||
page-height="100pt" page-width="150pt"> | |||
<fo:region-body margin="12pt" display-align="center" background-color="#FFFFF0"/> | |||
<fo:region-before region-name="odd-page-before" extent="10pt" precedence="true" | |||
display-align="after"/> | |||
<fo:region-after region-name="odd-page-after" extent="10pt" precedence="true"/> | |||
<fo:region-start region-name="odd-page-start" extent="10pt" reference-orientation="90" | |||
display-align="after"/> | |||
<fo:region-end region-name="odd-page-end" extent="10pt" reference-orientation="-90" | |||
display-align="after"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master master-name="even-page" | |||
page-height="100pt" page-width="150pt"> | |||
<fo:region-body margin="12pt" display-align="center" background-color="#F0FFF0"/> | |||
<fo:region-before region-name="even-page-before" extent="10pt" precedence="true" | |||
display-align="after"/> | |||
<fo:region-after region-name="even-page-after" extent="10pt" precedence="true"/> | |||
<fo:region-start region-name="even-page-start" extent="10pt" reference-orientation="90" | |||
display-align="after"/> | |||
<fo:region-end region-name="even-page-end" extent="10pt" reference-orientation="-90" | |||
display-align="after"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master master-name="blank-page" | |||
page-height="100pt" page-width="150pt"> | |||
<fo:region-body margin="12pt" display-align="center" background-color="#F0F0F0"/> | |||
<fo:region-before region-name="blank-page-before" extent="10pt" precedence="true" | |||
display-align="after"/> | |||
<fo:region-after region-name="blank-page-after" extent="10pt" precedence="true"/> | |||
<fo:region-start region-name="blank-page-start" extent="10pt" reference-orientation="90" | |||
display-align="after"/> | |||
<fo:region-end region-name="blank-page-end" extent="10pt" reference-orientation="-90" | |||
display-align="after"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master master-name="last-page" | |||
page-height="100pt" page-width="150pt"> | |||
<fo:region-body margin="45pt 12pt" display-align="center" background-color="#F0F0FF"/> | |||
<fo:region-before region-name="last-page-before" extent="10pt" precedence="true" | |||
display-align="after"/> | |||
<fo:region-after region-name="last-page-after" extent="10pt" precedence="true"/> | |||
<fo:region-start region-name="last-page-start" extent="10pt" reference-orientation="90" | |||
display-align="after"/> | |||
<fo:region-end region-name="last-page-end" extent="10pt" reference-orientation="-90" | |||
display-align="after"/> | |||
</fo:simple-page-master> | |||
<fo:page-sequence-master master-name="pages"> | |||
<fo:repeatable-page-master-alternatives> | |||
<fo:conditional-page-master-reference page-position="first" master-reference="first-page"/> | |||
<fo:conditional-page-master-reference page-position="last" master-reference="last-page"/> | |||
<fo:conditional-page-master-reference blank-or-not-blank="blank" | |||
master-reference="blank-page"/> | |||
<fo:conditional-page-master-reference odd-or-even="odd" master-reference="odd-page"/> | |||
<fo:conditional-page-master-reference odd-or-even="even" master-reference="even-page"/> | |||
</fo:repeatable-page-master-alternatives> | |||
</fo:page-sequence-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="pages" force-page-count="even" font-size="4pt" | |||
text-align="center"> | |||
<fo:static-content flow-name="first-page-before"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid red" | |||
padding-bottom="0.5pt">First Page Before.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="first-page-after"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid red" | |||
padding-top="0.5pt">First Page After.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="first-page-start"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" | |||
padding-bottom="0.5pt">First Page Start.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="first-page-end"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" | |||
padding-bottom="0.5pt">First Page End.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="odd-page-after"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid orange" | |||
padding-top="0.5pt">Odd Page After.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="odd-page-end"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" | |||
padding-bottom="0.5pt">Odd Page End.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="odd-page-start"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" | |||
padding-bottom="0.5pt">Odd Page Start.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="odd-page-before"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid orange" | |||
padding-bottom="0.5pt">Odd Page Before.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="even-page-end"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" | |||
padding-bottom="0.5pt">Even Page End.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="even-page-start"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" | |||
padding-bottom="0.5pt">Even Page Start.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="even-page-after"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid green" | |||
padding-top="0.5pt">Even Page After.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="even-page-before"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid green" | |||
padding-bottom="0.5pt">Even Page Before.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="blank-page-start"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" | |||
padding-bottom="0.5pt">Blank Page Start.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="blank-page-after"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid black" | |||
padding-top="0.5pt">Blank Page After.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="blank-page-before"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid black" | |||
padding-bottom="0.5pt">Blank Page Before.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="blank-page-end"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" | |||
padding-bottom="0.5pt">Blank Page End.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="last-page-before"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid blue" | |||
padding-bottom="0.5pt">Last Page Before.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="last-page-end"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" | |||
padding-bottom="0.5pt">Last Page End.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="last-page-after"> | |||
<fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid blue" | |||
padding-top="0.5pt">Last Page After.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="last-page-start"> | |||
<fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" | |||
padding-bottom="0.5pt">Last Page Start.</fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="xsl-footnote-separator"> | |||
<fo:block> | |||
<fo:leader leader-pattern="rule" leader-length="40%" rule-thickness="0.5pt"/> | |||
</fo:block> | |||
</fo:static-content> | |||
<fo:flow flow-name="xsl-region-body" font-size="8pt" line-height="10pt"> | |||
<fo:block>Apache™ FOP (Formatting Objects Processor) is a print formatter driven by XSL | |||
formatting objects (XSL-FO) and an output independent formatter.</fo:block> | |||
<fo:block break-before="page">It is an application<fo:footnote><fo:inline>*</fo:inline> | |||
<fo:footnote-body><fo:block font-size="80%">* written in | |||
Java</fo:block></fo:footnote-body></fo:footnote> that reads a formatting object (FO) | |||
tree and renders the resulting pages to a specified output.</fo:block> | |||
<fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which | |||
is a wider community of users and developers of open source projects.</fo:block> | |||
<fo:block break-before="page">Apache™ FOP (Formatting Objects Processor) is a print formatter | |||
driven by XSL formatting objects (XSL-FO) and an output independent formatter.</fo:block> | |||
<fo:block break-before="page">It is a Java application that reads a formatting object (FO) | |||
tree and renders the resulting pages to a specified output.</fo:block> | |||
<fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which | |||
is a wider community of users and developers of open source projects.</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |
@@ -0,0 +1,120 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.fonts; | |||
import java.util.BitSet; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import org.apache.fop.util.CharUtilities; | |||
public class CIDFullTestCase { | |||
private CIDFull cidFull; | |||
private MultiByteFont mbFont; | |||
private BitSet bs; | |||
private char[] chars; | |||
private int[] widths; | |||
private Map<Integer, Integer> glyphs; | |||
@Before | |||
public void setup() { | |||
bs = new BitSet(); | |||
glyphs = new HashMap<Integer, Integer>(); | |||
chars = new char[18]; | |||
widths = new int[18]; | |||
int i = 0; | |||
for (int j = 0; j < 20; j++) { | |||
if (j == 10 || j == 11) { | |||
continue; | |||
} | |||
bs.set(j); | |||
glyphs.put(Integer.valueOf(j), Integer.valueOf(j)); | |||
chars[i] = (char) j; | |||
widths[i] = 100; | |||
i++; | |||
} | |||
mbFont = mock(MultiByteFont.class); | |||
when(mbFont.getGlyphIndices()).thenReturn(bs); | |||
when(mbFont.getChars()).thenReturn(chars); | |||
when(mbFont.getWidths()).thenReturn(widths); | |||
cidFull = new CIDFull(mbFont); | |||
} | |||
@Test | |||
public void testGetOriginalGlyphIndex() { | |||
// index 5 exists | |||
assertEquals(cidFull.getOriginalGlyphIndex(5), 5); | |||
} | |||
@Test | |||
public void testGetUnicode() { | |||
// index 9 exists | |||
assertEquals(cidFull.getUnicode(9), (char) 9); | |||
// index 10 does not | |||
assertEquals(cidFull.getUnicode(10), CharUtilities.NOT_A_CHARACTER); | |||
} | |||
@Test | |||
public void testMapChar() { | |||
// index 9 exists | |||
char c = 'a'; | |||
assertEquals(cidFull.mapChar(9, c), (char) 9); | |||
} | |||
@Test | |||
public void testGetGlyphs() { | |||
Map<Integer, Integer> fontGlyphs = cidFull.getGlyphs(); | |||
for (Integer key : fontGlyphs.keySet()) { | |||
assertEquals(fontGlyphs.get(key), glyphs.get(key)); | |||
} | |||
assertTrue(fontGlyphs.size() == glyphs.size()); | |||
} | |||
@Test | |||
public void testGetChars() { | |||
assertArrayEquals(cidFull.getChars(), chars); | |||
} | |||
@Test | |||
public void testGetNumberOfGlyphs() { | |||
assertTrue(cidFull.getNumberOfGlyphs() == 20); | |||
} | |||
@Test | |||
public void testGetGlyphIndices() { | |||
assertEquals(bs, cidFull.getGlyphIndices()); | |||
} | |||
@Test | |||
public void testGetWidths() { | |||
assertArrayEquals(cidFull.getWidths(), widths); | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.apache.fop.fonts; | |||
import java.net.URI; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.InOrder; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.inOrder; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
public class FontManagerTestCase { | |||
private FontManager sut; | |||
private FontCacheManager fontCacheManager; | |||
private FontDetector fontDetector; | |||
private InternalResourceResolver resolver; | |||
@Before | |||
public void setUp() { | |||
resolver = mock(InternalResourceResolver.class); | |||
fontCacheManager = mock(FontCacheManager.class); | |||
fontDetector = mock(FontDetector.class); | |||
sut = new FontManager(resolver, fontDetector, fontCacheManager); | |||
} | |||
@Test | |||
public void testSetCacheFile() { | |||
URI testURI = URI.create("test/uri"); | |||
sut.setCacheFile(testURI); | |||
InOrder inOrder = inOrder(resolver, fontCacheManager); | |||
inOrder.verify(resolver).resolveFromBase(testURI); | |||
inOrder.verify(fontCacheManager).setCacheFile(any(URI.class)); | |||
} | |||
@Test | |||
public void testGetFontCache() { | |||
sut.getFontCache(); | |||
verify(fontCacheManager).load(); | |||
} | |||
@Test | |||
public void testSaveCache() throws FOPException { | |||
sut.saveCache(); | |||
verify(fontCacheManager).save(); | |||
} | |||
@Test | |||
public void testDeleteCache() throws FOPException { | |||
sut.deleteCache(); | |||
verify(fontCacheManager).delete(); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.apache.fop.layoutmgr; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import org.apache.fop.fo.Constants; | |||
public class BreakElementTestCase { | |||
/** | |||
* Tests that the constructor sets the break class to a valid default value. | |||
*/ | |||
@Test | |||
public void breakClassMustBeValid() { | |||
LayoutContext context = LayoutContext.newInstance(); | |||
BreakElement breakElement = new BreakElement(new Position(null), 0, context); | |||
assertEquals(Constants.EN_AUTO, breakElement.getBreakClass()); | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package org.apache.fop.pdf; | |||
import org.junit.Test; | |||
import org.apache.fop.fonts.CodePointMapping; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
public class PDFEncodingTestCase { | |||
/** | |||
* Tests the createPDFEncoding method to ensure a null encoding type | |||
* is handled correctly. | |||
*/ | |||
@Test | |||
public void testCreatePDFEncodingForNull() { | |||
Object encoding = PDFEncoding.createPDFEncoding(null, "Test"); | |||
assertEquals(encoding, null); | |||
} | |||
/** | |||
* Tests that when a PDFEncoding object is created, if the encoding type is | |||
* that of StandardEncoding, the baseEncoding tag is omitted. | |||
*/ | |||
@Test | |||
public void testStandardEncodingDiffs() { | |||
Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( | |||
CodePointMapping.SYMBOL_ENCODING), "Test"); | |||
if (encoding instanceof PDFEncoding) { | |||
PDFEncoding pdfEncoding = (PDFEncoding) encoding; | |||
assertFalse(pdfEncoding.entries.containsKey("BaseEncoding")); | |||
} | |||
} | |||
/** | |||
* Tests that when the StandardEncoding type is provided and there are no | |||
* differences, the returned encoding object is null. | |||
*/ | |||
@Test | |||
public void testStandardEncodingNoDiff() { | |||
Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( | |||
CodePointMapping.STANDARD_ENCODING), "Test"); | |||
assertEquals(encoding, null); | |||
} | |||
/** | |||
* Tests that when the SymbolEncoding type is provided and there are no | |||
* differences, the returned encoding string is that of SymbolEncoding. | |||
*/ | |||
@Test | |||
public void testCreatePDFEncodingSymbol() { | |||
Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( | |||
CodePointMapping.SYMBOL_ENCODING), "Symbol"); | |||
assert (encoding instanceof String); | |||
String pdfEncoding = (String) encoding; | |||
assertEquals(pdfEncoding, "SymbolEncoding"); | |||
} | |||
} |