git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1394098 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_0
@@ -43,6 +43,7 @@ import org.apache.xmlgraphics.image.loader.ImageSize; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; | |||
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; | |||
import org.apache.xmlgraphics.image.loader.util.ImageUtil; | |||
import org.apache.xmlgraphics.io.XmlSourceUtil; | |||
import org.apache.fop.util.DefaultErrorListener; | |||
import org.apache.fop.util.UnclosableInputStream; |
@@ -18,7 +18,6 @@ | |||
# Shell script to run FOP, adapted from the Jakarta-Ant project. | |||
rpm_mode=true | |||
java_exec_args="-Djava.awt.headless=true" | |||
fop_exec_args= | |||
no_config=false | |||
fop_exec_debug=false | |||
@@ -248,7 +247,7 @@ fi | |||
# Execute FOP using eval/exec to preserve spaces in paths, | |||
# java options, and FOP args | |||
fop_exec_command="exec \"$JAVACMD\" $java_exec_args $LOGCHOICE $LOGLEVEL -classpath \"$LOCALCLASSPATH\" $FOP_OPTS org.apache.fop.cli.Main $fop_exec_args" | |||
fop_exec_command="exec \"$JAVACMD\" $LOGCHOICE $LOGLEVEL -classpath \"$LOCALCLASSPATH\" $FOP_OPTS org.apache.fop.cli.Main $fop_exec_args" | |||
if $fop_exec_debug ; then | |||
echo $fop_exec_command | |||
fi |
@@ -58,7 +58,7 @@ set LIBDIR=%LOCAL_FOP_HOME%lib | |||
set LOCALCLASSPATH=%FOP_HYPHENATION_PATH% | |||
for %%l in (%LOCAL_FOP_HOME%build\*.jar %LIBDIR%\*.jar) do set LOCALCLASSPATH=!LOCALCLASSPATH!;%%l | |||
set JAVAOPTS=-Denv.windir=%WINDIR% -Djava.awt.headless=true | |||
set JAVAOPTS=-Denv.windir=%WINDIR% | |||
if "%JAVA_HOME%" == "" goto noJavaHome | |||
if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome |
@@ -18,7 +18,6 @@ | |||
// rpm_mode is irrelevant on Windows | |||
// var rpm_mode=true; | |||
var java_exec_args = "-Djava.awt.headless=true"; | |||
var fop_exec_args = ""; | |||
var no_config=false; | |||
var fop_exec_debug=false; | |||
@@ -63,7 +62,6 @@ function read_args() { | |||
WScript.Echo("keepopen: " + keep_open); | |||
WScript.Echo("noconfig: " + no_config); | |||
WScript.Echo("help: " + show_help); | |||
WScript.Echo("java arguments: " + java_exec_args); | |||
WScript.Echo("fop arguments: " + fop_exec_args); | |||
} | |||
} | |||
@@ -222,7 +220,6 @@ function get_local_classpath() { | |||
// Execute fop via shell.Exec | |||
function fop_exec() { | |||
var fop_exec_command = "\"" + javacmd + "\" " | |||
+ java_exec_args + " " | |||
+ (config.Exists("JAVA_OPTS")?config.Item("JAVA_OPTS") + " ":"") | |||
+ (config.Exists("LOGCHOICE")?config.Item("LOGCHOICE") + " ":"") | |||
+ (config.Exists("LOGLEVEL")?config.Item("LOGLEVEL") + " ":"") | |||
@@ -255,7 +252,6 @@ function fop_exec() { | |||
function fop_run() { | |||
var fop_exec_command = "cmd /" + (keep_open?"K":"C") + " \"" | |||
+ "\"" + javacmd + "\" " | |||
+ java_exec_args + " " | |||
+ (config.Exists("JAVA_OPTS")?config.Item("JAVA_OPTS") + " ":"") | |||
+ (config.Exists("LOGCHOICE")?config.Item("LOGCHOICE") + " ":"") | |||
+ (config.Exists("LOGLEVEL")?config.Item("LOGLEVEL") + " ":"") |
@@ -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); | |||
} | |||
}); | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.apache.fop.accessibility.fo; | |||
import java.util.Locale; | |||
import java.util.Stack; | |||
import org.xml.sax.SAXException; | |||
import org.xml.sax.helpers.AttributesImpl; | |||
@@ -30,6 +31,7 @@ import org.apache.fop.fo.FOEventHandler; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FOText; | |||
import org.apache.fop.fo.extensions.ExtensionElementMapping; | |||
import org.apache.fop.fo.extensions.InternalElementMapping; | |||
import org.apache.fop.fo.flow.AbstractGraphics; | |||
import org.apache.fop.fo.flow.BasicLink; | |||
import org.apache.fop.fo.flow.Block; | |||
@@ -55,6 +57,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 +70,12 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
private StructureTreeEventHandler structureTreeEventHandler; | |||
private LayoutMasterSet layoutMasterSet; | |||
private final Stack<Table> tables = new Stack<Table>(); | |||
private final Stack<Boolean> inTableHeader = new Stack<Boolean>(); | |||
public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { | |||
this.structureTreeEventHandler = structureTreeEventHandler; | |||
} | |||
@@ -81,6 +90,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 +140,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 | |||
@@ -170,42 +201,51 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
@Override | |||
public void startTable(Table tbl) { | |||
tables.push(tbl); | |||
startElement(tbl); | |||
} | |||
@Override | |||
public void endTable(Table tbl) { | |||
endElement(tbl); | |||
tables.pop(); | |||
} | |||
@Override | |||
public void startHeader(TableHeader header) { | |||
inTableHeader.push(Boolean.TRUE); | |||
startElement(header); | |||
} | |||
@Override | |||
public void endHeader(TableHeader header) { | |||
endElement(header); | |||
inTableHeader.pop(); | |||
} | |||
@Override | |||
public void startFooter(TableFooter footer) { | |||
// TODO Shouldn't it be true? | |||
inTableHeader.push(Boolean.FALSE); | |||
startElement(footer); | |||
} | |||
@Override | |||
public void endFooter(TableFooter footer) { | |||
endElement(footer); | |||
inTableHeader.pop(); | |||
} | |||
@Override | |||
public void startBody(TableBody body) { | |||
inTableHeader.push(Boolean.FALSE); | |||
startElement(body); | |||
} | |||
@Override | |||
public void endBody(TableBody body) { | |||
endElement(body); | |||
inTableHeader.pop(); | |||
} | |||
@Override | |||
@@ -223,6 +263,24 @@ class StructureTreeEventTrigger extends FOEventHandler { | |||
AttributesImpl attributes = new AttributesImpl(); | |||
addSpanAttribute(attributes, "number-columns-spanned", tc.getNumberColumnsSpanned()); | |||
addSpanAttribute(attributes, "number-rows-spanned", tc.getNumberRowsSpanned()); | |||
boolean rowHeader = inTableHeader.peek(); | |||
boolean columnHeader = tables.peek().getColumn(tc.getColumnNumber() - 1).isHeader(); | |||
if (rowHeader || columnHeader) { | |||
final String th = "TH"; | |||
String role = tc.getCommonAccessibility().getRole(); | |||
/* Do not override a custom role */ | |||
if (role == null) { | |||
role = th; | |||
addNoNamespaceAttribute(attributes, "role", th); | |||
} | |||
if (role.equals(th)) { | |||
if (columnHeader) { | |||
String scope = rowHeader ? "Both" : "Row"; | |||
addAttribute(attributes, InternalElementMapping.URI, InternalElementMapping.SCOPE, | |||
InternalElementMapping.STANDARD_PREFIX, scope); | |||
} | |||
} | |||
} | |||
startElement(tc, attributes); | |||
} | |||
@@ -277,16 +335,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); |
@@ -32,10 +32,11 @@ import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.io.TempResourceURIGenerator; | |||
import org.apache.fop.afp.modca.ResourceGroup; | |||
import org.apache.fop.afp.modca.StreamedResourceGroup; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.TempResourceURIGenerator; | |||
/** | |||
* Manages the streaming of the AFP output |
@@ -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; | |||
} | |||
@@ -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. | |||
* |
@@ -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; | |||
} | |||
} | |||
} |
@@ -1,64 +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.afp.modca; | |||
/** | |||
* The TagLogicalElementBean provides a bean for holding the attributes of | |||
* a tag logical element as key value pairs. | |||
* <p/> | |||
*/ | |||
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; | |||
} | |||
/** | |||
* Getter for the value attribute. | |||
* | |||
* @return the value | |||
*/ | |||
public String getValue() { | |||
return value; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -106,7 +106,7 @@ public final class AFPResourceAccessor { | |||
URI resolveURI(String uri); | |||
} | |||
private final class NullBaseURIResolver implements URIResolver { | |||
private static final class NullBaseURIResolver implements URIResolver { | |||
public URI resolveURI(URI uri) { | |||
return uri; |
@@ -21,7 +21,9 @@ package org.apache.fop.apps; | |||
import java.net.URI; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.fonts.FontManager; | |||
/** | |||
@@ -52,4 +54,7 @@ public interface EnvironmentProfile { | |||
* @return the default base URI | |||
*/ | |||
URI getDefaultBaseURI(); | |||
/** @see FopFactoryConfig#getFallbackResolver() */ | |||
FallbackResolver getFallbackResolver(); | |||
} |
@@ -21,8 +21,12 @@ package org.apache.fop.apps; | |||
import java.net.URI; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.RestrictedFallbackResolver; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.UnrestrictedFallbackResolver; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.FontCacheManager; | |||
import org.apache.fop.fonts.FontCacheManagerFactory; | |||
@@ -51,7 +55,8 @@ public final class EnvironmentalProfileFactory { | |||
return new Profile(defaultBaseUri, resourceResolver, | |||
createFontManager(defaultBaseUri, resourceResolver, | |||
FontDetectorFactory.createDefault(), | |||
FontCacheManagerFactory.createDefault())); | |||
FontCacheManagerFactory.createDefault()), | |||
new UnrestrictedFallbackResolver()); | |||
} | |||
/** | |||
@@ -67,7 +72,8 @@ public final class EnvironmentalProfileFactory { | |||
return new Profile(defaultBaseUri, resourceResolver, | |||
createFontManager(defaultBaseUri, resourceResolver, | |||
FontDetectorFactory.createDisabled(), | |||
FontCacheManagerFactory.createDisabled())); | |||
FontCacheManagerFactory.createDisabled()), | |||
new RestrictedFallbackResolver()); | |||
} | |||
private static final class Profile implements EnvironmentProfile { | |||
@@ -78,8 +84,10 @@ public final class EnvironmentalProfileFactory { | |||
private final URI defaultBaseURI; | |||
private final FallbackResolver fallbackResolver; | |||
private Profile(URI defaultBaseURI, ResourceResolver resourceResolver, | |||
FontManager fontManager) { | |||
FontManager fontManager, FallbackResolver fallbackResolver) { | |||
if (defaultBaseURI == null) { | |||
throw new IllegalArgumentException("Default base URI must not be null"); | |||
} | |||
@@ -92,6 +100,7 @@ public final class EnvironmentalProfileFactory { | |||
this.defaultBaseURI = defaultBaseURI; | |||
this.resourceResolver = resourceResolver; | |||
this.fontManager = fontManager; | |||
this.fallbackResolver = fallbackResolver; | |||
} | |||
public ResourceResolver getResourceResolver() { | |||
@@ -105,6 +114,10 @@ public final class EnvironmentalProfileFactory { | |||
public URI getDefaultBaseURI() { | |||
return defaultBaseURI; | |||
} | |||
public FallbackResolver getFallbackResolver() { | |||
return fallbackResolver; | |||
} | |||
} | |||
private static FontManager createFontManager(URI defaultBaseUri, ResourceResolver resourceResolver, |
@@ -131,21 +131,7 @@ public class FOUserAgent { | |||
/** Set of keywords applicable to this document. */ | |||
protected String keywords = null; | |||
private ImageSessionContext imageSessionContext = new AbstractImageSessionContext() { | |||
public ImageContext getParentContext() { | |||
return factory; | |||
} | |||
public float getTargetResolution() { | |||
return FOUserAgent.this.getTargetResolution(); | |||
} | |||
public Source resolveURI(String uri) { | |||
return FOUserAgent.this.resolveURI(uri); | |||
} | |||
}; | |||
private final ImageSessionContext imageSessionContext; | |||
/** | |||
* Main constructor. <b>This constructor should not be called directly. Please use the | |||
@@ -154,11 +140,25 @@ public class FOUserAgent { | |||
* @param resourceResolver the resolver used to acquire resources | |||
* @see org.apache.fop.apps.FopFactory | |||
*/ | |||
FOUserAgent(FopFactory factory, InternalResourceResolver resourceResolver) { | |||
FOUserAgent(final FopFactory factory, InternalResourceResolver resourceResolver) { | |||
this.factory = factory; | |||
this.resourceResolver = resourceResolver; | |||
setTargetResolution(factory.getTargetResolution()); | |||
setAccessibility(factory.isAccessibilityEnabled()); | |||
imageSessionContext = new AbstractImageSessionContext(factory.getFallbackResolver()) { | |||
public ImageContext getParentContext() { | |||
return factory; | |||
} | |||
public float getTargetResolution() { | |||
return FOUserAgent.this.getTargetResolution(); | |||
} | |||
public Source resolveURI(String uri) { | |||
return FOUserAgent.this.resolveURI(uri); | |||
} | |||
}; | |||
} | |||
/** |
@@ -39,9 +39,9 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry; | |||
import org.apache.xmlgraphics.image.loader.util.Penalty; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.FontManagerConfigurator; | |||
import org.apache.fop.hyphenation.HyphenationTreeCache; |
@@ -36,6 +36,7 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.image.loader.ImageContext; | |||
import org.apache.xmlgraphics.image.loader.ImageManager; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; | |||
import org.apache.xmlgraphics.util.UnitConv; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
@@ -418,6 +419,11 @@ public final class FopFactory implements ImageContext { | |||
return config.getFontManager(); | |||
} | |||
/** @see FopFactoryConfig#getFallbackResolver() */ | |||
FallbackResolver getFallbackResolver() { | |||
return config.getFallbackResolver(); | |||
} | |||
/** | |||
* Returns the color space cache for this instance. | |||
* <p> |
@@ -30,8 +30,9 @@ import org.apache.avalon.framework.configuration.Configuration; | |||
import org.apache.xmlgraphics.image.loader.ImageContext; | |||
import org.apache.xmlgraphics.image.loader.ImageManager; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.FontManager; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
@@ -464,6 +465,10 @@ public final class FopFactoryBuilder { | |||
public Map<String, String> getHyphenationPatternNames() { | |||
return hyphPatNames; | |||
} | |||
public FallbackResolver getFallbackResolver() { | |||
return enviro.getFallbackResolver(); | |||
} | |||
} | |||
private interface FopFactoryConfigBuilder { |
@@ -26,8 +26,9 @@ import java.util.Set; | |||
import org.apache.avalon.framework.configuration.Configuration; | |||
import org.apache.xmlgraphics.image.loader.ImageManager; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.fop.fonts.FontManager; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
@@ -163,4 +164,11 @@ public interface FopFactoryConfig { | |||
/** @return the hyphenation pattern names */ | |||
Map<String, String> getHyphenationPatternNames(); | |||
/** | |||
* Controls the mechanisms that are used in the event that {@link javax.xml.transform.Source} | |||
* used for resources couldn't be read. | |||
* @return the fallback resolver | |||
*/ | |||
FallbackResolver getFallbackResolver(); | |||
} |
@@ -29,12 +29,14 @@ import javax.xml.transform.Source; | |||
import javax.xml.transform.TransformerException; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.apache.xmlgraphics.io.Resource; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.xmlgraphics.util.uri.DataURIResolver; | |||
/** | |||
* This object holds the base URI from which to resolve URIs against as well as the resolver for | |||
* resource acquisition. It also does some URI sanitization of common URI syntactical errors. This | |||
* class takes in a {@link org.apache.fop.apps.io.ResourceResolver} and delegates all relevant | |||
* class takes in a {@link org.apache.xmlgraphics.io.ResourceResolver} and delegates all relevant | |||
* URIs to it. | |||
*/ | |||
public class InternalResourceResolver { |
@@ -1,51 +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.apps.io; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.net.URI; | |||
/** | |||
* Implementations of this resource resolver allow FOP users to control the URI resolution | |||
* mechanism. All resource and output stream acquisition goes through this when its implementation | |||
* is given to the {@link org.apache.fop.apps.EnvironmentProfile}. | |||
*/ | |||
public interface ResourceResolver { | |||
/** | |||
* Get a resource given the URI pointing to said resource. | |||
* | |||
* @param uri the resource URI | |||
* @return the resource | |||
* @throws IOException if an I/O error occured during resource acquisition | |||
*/ | |||
Resource getResource(URI uri) throws IOException; | |||
/** | |||
* Gets an output stream of a given URI. | |||
* | |||
* @param uri the output stream URI | |||
* @return the output stream | |||
* @throws IOException if an I/O error occured while creating an output stream | |||
*/ | |||
OutputStream getOutputStream(URI uri) throws IOException; | |||
} |
@@ -28,6 +28,11 @@ import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.xmlgraphics.io.Resource; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.xmlgraphics.io.TempResourceResolver; | |||
import org.apache.xmlgraphics.io.TempResourceURIGenerator; | |||
/** | |||
* A factory class for {@link ResourceResolver}s. | |||
*/ | |||
@@ -70,10 +75,10 @@ public final class ResourceResolverFactory { | |||
} | |||
/** | |||
* Creates a temporary-resource-schema aware resource resolver. Temporary resource URIs are | |||
* Creates a temporary-resource-scheme aware resource resolver. Temporary resource URIs are | |||
* created by {@link TempResourceURIGenerator}. | |||
* | |||
* @param tempResourceResolver the temporary-resource-schema resolver to use | |||
* @param tempResourceResolver the temporary-resource-scheme resolver to use | |||
* @param defaultResourceResolver the default resource resolver to use | |||
* @return the ressource resolver | |||
*/ | |||
@@ -84,17 +89,17 @@ public final class ResourceResolverFactory { | |||
} | |||
/** | |||
* 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 | |||
* This creates the builder class for binding URI schemes to implementations of | |||
* {@link ResourceResolver}. This allows users to define their own URI schemes 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 | |||
* none of the other registered resolvers match the scheme | |||
* @return the scheme aware {@link ResourceResolver} builder | |||
*/ | |||
public static SchemaAwareResourceResolverBuilder createSchemaAwareResourceResolverBuilder( | |||
public static SchemeAwareResourceResolverBuilder createSchemeAwareResourceResolverBuilder( | |||
ResourceResolver defaultResolver) { | |||
return new SchemaAwareResourceResolverBuilderImpl(defaultResolver); | |||
return new SchemeAwareResourceResolverBuilderImpl(defaultResolver); | |||
} | |||
private static final class DefaultResourceResolver implements ResourceResolver { | |||
@@ -132,13 +137,13 @@ public final class ResourceResolverFactory { | |||
this.defaultResourceResolver = defaultResourceResolver; | |||
} | |||
private static boolean isTempUri(URI uri) { | |||
return TempResourceURIGenerator.isTempUri(uri); | |||
private static boolean isTempURI(URI uri) { | |||
return TempResourceURIGenerator.isTempURI(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public Resource getResource(URI uri) throws IOException { | |||
if (isTempUri(uri)) { | |||
if (isTempURI(uri)) { | |||
return tempResourceResolver.getResource(uri.getPath()); | |||
} else { | |||
return defaultResourceResolver.getResource(uri); | |||
@@ -147,7 +152,7 @@ public final class ResourceResolverFactory { | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(URI uri) throws IOException { | |||
if (isTempUri(uri)) { | |||
if (isTempURI(uri)) { | |||
return tempResourceResolver.getOutputStream(uri.getPath()); | |||
} else { | |||
return defaultResourceResolver.getOutputStream(uri); | |||
@@ -188,23 +193,23 @@ public final class ResourceResolverFactory { | |||
} | |||
} | |||
private static final class SchemaAwareResourceResolver implements ResourceResolver { | |||
private static final class SchemeAwareResourceResolver implements ResourceResolver { | |||
private final Map<String, ResourceResolver> schemaHandlingResourceResolvers; | |||
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers; | |||
private final ResourceResolver defaultResolver; | |||
private SchemaAwareResourceResolver( | |||
Map<String, ResourceResolver> schemaHandlingResourceResolvers, | |||
private SchemeAwareResourceResolver( | |||
Map<String, ResourceResolver> schemEHandlingResourceResolvers, | |||
ResourceResolver defaultResolver) { | |||
this.schemaHandlingResourceResolvers = schemaHandlingResourceResolvers; | |||
this.schemeHandlingResourceResolvers = schemEHandlingResourceResolvers; | |||
this.defaultResolver = defaultResolver; | |||
} | |||
private ResourceResolver getResourceResolverForSchema(URI uri) { | |||
String schema = uri.getScheme(); | |||
if (schemaHandlingResourceResolvers.containsKey(schema)) { | |||
return schemaHandlingResourceResolvers.get(schema); | |||
private ResourceResolver getResourceResolverForScheme(URI uri) { | |||
String scheme = uri.getScheme(); | |||
if (schemeHandlingResourceResolvers.containsKey(scheme)) { | |||
return schemeHandlingResourceResolvers.get(scheme); | |||
} else { | |||
return defaultResolver; | |||
} | |||
@@ -212,58 +217,58 @@ public final class ResourceResolverFactory { | |||
/** {@inheritDoc} */ | |||
public Resource getResource(URI uri) throws IOException { | |||
return getResourceResolverForSchema(uri).getResource(uri); | |||
return getResourceResolverForScheme(uri).getResource(uri); | |||
} | |||
/** {@inheritDoc} */ | |||
public OutputStream getOutputStream(URI uri) throws IOException { | |||
return getResourceResolverForSchema(uri).getOutputStream(uri); | |||
return getResourceResolverForScheme(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 | |||
* URI schemes 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); | |||
* SchemeAwareResourceResolverBuilder builder | |||
* = ResourceResolverFactory.createSchemeAwareResourceResolverBuilder(defaultResolver); | |||
* builder.registerResourceResolverForScheme("test", testResolver); | |||
* builder.registerResourceResolverForScheme("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 { | |||
public interface SchemeAwareResourceResolverBuilder { | |||
/** | |||
* Register a schema with its respective {@link ResourceResolver}. This resolver will be | |||
* used as the only resolver for the specified schema. | |||
* Register a scheme with its respective {@link ResourceResolver}. This resolver will be | |||
* used as the only resolver for the specified scheme. | |||
* | |||
* @param schema the schema to be used with the given resolver | |||
* @param scheme the scheme to be used with the given resolver | |||
* @param resourceResolver the resource resolver | |||
*/ | |||
void registerResourceResolverForSchema(String schema, ResourceResolver resourceResolver); | |||
void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver); | |||
/** | |||
* Builds a {@link ResourceResolver} that will delegate to the respective resource resolver | |||
* when a registered URI schema is given | |||
* when a registered URI scheme is given | |||
* | |||
* @return a resolver that delegates to the appropriate schema resolver | |||
* @return a resolver that delegates to the appropriate scheme resolver | |||
*/ | |||
ResourceResolver build(); | |||
} | |||
private static final class CompletedSchemaAwareResourceResolverBuilder | |||
implements SchemaAwareResourceResolverBuilder { | |||
private static final class CompletedSchemeAwareResourceResolverBuilder | |||
implements SchemeAwareResourceResolverBuilder { | |||
private static final SchemaAwareResourceResolverBuilder INSTANCE | |||
= new CompletedSchemaAwareResourceResolverBuilder(); | |||
private static final SchemeAwareResourceResolverBuilder INSTANCE | |||
= new CompletedSchemeAwareResourceResolverBuilder(); | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
@@ -271,59 +276,58 @@ public final class ResourceResolverFactory { | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
public void registerResourceResolverForScheme(String scheme, | |||
ResourceResolver resourceResolver) { | |||
throw new IllegalStateException("Resource resolver already built"); | |||
} | |||
} | |||
private static final class ActiveSchemaAwareResourceResolverBuilder | |||
implements SchemaAwareResourceResolverBuilder { | |||
private static final class ActiveSchemeAwareResourceResolverBuilder | |||
implements SchemeAwareResourceResolverBuilder { | |||
private final Map<String, ResourceResolver> schemaHandlingResourceResolvers | |||
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers | |||
= new HashMap<String, ResourceResolver>(); | |||
private final ResourceResolver defaultResolver; | |||
private ActiveSchemaAwareResourceResolverBuilder(ResourceResolver defaultResolver) { | |||
private ActiveSchemeAwareResourceResolverBuilder(ResourceResolver defaultResolver) { | |||
this.defaultResolver = defaultResolver; | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
public void registerResourceResolverForScheme(String scheme, | |||
ResourceResolver resourceResolver) { | |||
schemaHandlingResourceResolvers.put(schema, resourceResolver); | |||
schemeHandlingResourceResolvers.put(scheme, resourceResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
return new SchemaAwareResourceResolver( | |||
Collections.unmodifiableMap(schemaHandlingResourceResolvers), defaultResolver); | |||
return new SchemeAwareResourceResolver( | |||
Collections.unmodifiableMap(schemeHandlingResourceResolvers), defaultResolver); | |||
} | |||
} | |||
private static final class SchemaAwareResourceResolverBuilderImpl | |||
implements SchemaAwareResourceResolverBuilder { | |||
private static final class SchemeAwareResourceResolverBuilderImpl | |||
implements SchemeAwareResourceResolverBuilder { | |||
private SchemaAwareResourceResolverBuilder delegate; | |||
private SchemeAwareResourceResolverBuilder delegate; | |||
private SchemaAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) { | |||
this.delegate = new ActiveSchemaAwareResourceResolverBuilder(defaultResolver); | |||
private SchemeAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) { | |||
this.delegate = new ActiveSchemeAwareResourceResolverBuilder(defaultResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public void registerResourceResolverForSchema(String schema, | |||
public void registerResourceResolverForScheme(String scheme, | |||
ResourceResolver resourceResolver) { | |||
delegate.registerResourceResolverForSchema(schema, resourceResolver); | |||
delegate.registerResourceResolverForScheme(scheme, resourceResolver); | |||
} | |||
/** {@inheritDoc} */ | |||
public ResourceResolver build() { | |||
ResourceResolver resourceResolver = delegate.build(); | |||
delegate = CompletedSchemaAwareResourceResolverBuilder.INSTANCE; | |||
delegate = CompletedSchemeAwareResourceResolverBuilder.INSTANCE; | |||
return resourceResolver; | |||
} | |||
} | |||
} |
@@ -1,48 +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.apps.io; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
/** | |||
* Implementations of this interface resolve URIs for temporary files used by FOP. The temporary- | |||
* resource URI scheme comes from {@link TempResourceURIGenerator#TMP_SCHEMA}. | |||
*/ | |||
public interface TempResourceResolver { | |||
/** | |||
* Get a temporary-resource given the URI pointing to said resource. | |||
* | |||
* @param uri the resource URI | |||
* @return the resource | |||
* @throws IOException if an I/O error occured during resource acquisition | |||
*/ | |||
Resource getResource(String uri) throws IOException; | |||
/** | |||
* Gets an temporary-output stream of a given URI. | |||
* | |||
* @param uri the output stream URI | |||
* @return the output stream | |||
* @throws IOException if an I/O error occured while creating an output stream | |||
*/ | |||
OutputStream getOutputStream(String uri) throws IOException; | |||
} |
@@ -1,57 +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. | |||
*/ | |||
package org.apache.fop.apps.io; | |||
import java.net.URI; | |||
import java.util.concurrent.atomic.AtomicLong; | |||
/** | |||
* Creates a URI for any temporary resource used within FOP. | |||
*/ | |||
public final class TempResourceURIGenerator { | |||
public static final String TMP_SCHEMA = "tmp"; | |||
private final String tempURIPrefix; | |||
private final AtomicLong counter; | |||
/** | |||
* @param uriPrefix a prefix used to name the unique URI | |||
*/ | |||
public TempResourceURIGenerator(String uriPrefix) { | |||
counter = new AtomicLong(); | |||
tempURIPrefix = URI.create(TMP_SCHEMA + ":///" + uriPrefix).toASCIIString(); | |||
} | |||
/** | |||
* Generate a unique URI for a temporary resource | |||
* @return the URI | |||
*/ | |||
public URI generate() { | |||
return URI.create(tempURIPrefix + getUniqueId()); | |||
} | |||
private String getUniqueId() { | |||
return Long.toHexString(counter.getAndIncrement()); | |||
} | |||
public static boolean isTempUri(URI uri) { | |||
return TMP_SCHEMA.equals(uri.getScheme()); | |||
} | |||
} |
@@ -35,9 +35,10 @@ import org.xml.sax.SAXException; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.xmlgraphics.io.TempResourceURIGenerator; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.apps.io.TempResourceURIGenerator; | |||
import org.apache.fop.fonts.FontInfo; | |||
/** |
@@ -34,6 +34,10 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.flow.AbstractRetrieveMarker; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.Markers; | |||
import org.apache.fop.fo.pagination.SimplePageMaster; | |||
import org.apache.fop.traits.WritingModeTraitsGetter; | |||
@@ -81,13 +85,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable { | |||
private Map<String, List<PageViewport>> pendingResolved = null; | |||
// hashmap of markers for this page | |||
// start and end are added by the fo that contains the markers | |||
private Map<String, Marker> markerFirstStart = null; | |||
private Map<String, Marker> markerLastStart = null; | |||
private Map<String, Marker> markerFirstAny = null; | |||
private Map<String, Marker> markerLastEnd = null; | |||
private Map<String, Marker> markerLastAny = null; | |||
private Markers pageMarkers; | |||
/** | |||
* logging instance | |||
@@ -352,115 +350,23 @@ public class PageViewport extends AreaTreeObject implements Resolvable { | |||
} | |||
/** | |||
* Add the markers for this page. | |||
* Only the required markers are kept. | |||
* For "first-starting-within-page" it adds the markers | |||
* that are starting only if the marker class name is not | |||
* already added. | |||
* For "first-including-carryover" it adds any starting marker | |||
* if the marker class name is not already added. | |||
* For "last-starting-within-page" it adds all marks that | |||
* are starting, replacing earlier markers. | |||
* For "last-ending-within-page" it adds all markers that | |||
* are ending, replacing earlier markers. | |||
* | |||
* Should this logic be placed in the Page layout manager. | |||
* Register the markers for this page. | |||
* | |||
* @param marks the map of markers to add | |||
* @param starting if the area being added is starting or ending | |||
* @param isfirst if the area being added has is-first trait | |||
* @param islast if the area being added has is-last trait | |||
*/ | |||
public void addMarkers(Map<String, Marker> marks, boolean starting, | |||
boolean isfirst, boolean islast) { | |||
if (marks == null) { | |||
return; | |||
} | |||
if (log.isDebugEnabled()) { | |||
log.debug("--" + marks.keySet() + ": " | |||
+ (starting ? "starting" : "ending") | |||
+ (isfirst ? ", first" : "") | |||
+ (islast ? ", last" : "")); | |||
} | |||
// at the start of the area, register is-first and any areas | |||
if (starting) { | |||
if (isfirst) { | |||
if (markerFirstStart == null) { | |||
markerFirstStart = new HashMap<String, Marker>(); | |||
} | |||
if (markerFirstAny == null) { | |||
markerFirstAny = new HashMap<String, Marker>(); | |||
} | |||
// first on page: only put in new values, leave current | |||
for (String key : marks.keySet()) { | |||
if (!markerFirstStart.containsKey(key)) { | |||
markerFirstStart.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding marker " + key + " to FirstStart"); | |||
} | |||
} | |||
if (!markerFirstAny.containsKey(key)) { | |||
markerFirstAny.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding marker " + key + " to FirstAny"); | |||
} | |||
} | |||
} | |||
if (markerLastStart == null) { | |||
markerLastStart = new HashMap<String, Marker>(); | |||
} | |||
// last on page: replace all | |||
markerLastStart.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding all markers to LastStart"); | |||
} | |||
} else { | |||
if (markerFirstAny == null) { | |||
markerFirstAny = new HashMap<String, Marker>(); | |||
} | |||
// first on page: only put in new values, leave current | |||
for (String key : marks.keySet()) { | |||
if (!markerFirstAny.containsKey(key)) { | |||
markerFirstAny.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding marker " + key + " to FirstAny"); | |||
} | |||
} | |||
} | |||
} | |||
} else { | |||
// at the end of the area, register is-last and any areas | |||
if (islast) { | |||
if (markerLastEnd == null) { | |||
markerLastEnd = new HashMap<String, Marker>(); | |||
} | |||
// last on page: replace all | |||
markerLastEnd.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding all markers to LastEnd"); | |||
} | |||
} | |||
if (markerLastAny == null) { | |||
markerLastAny = new HashMap<String, Marker>(); | |||
} | |||
// last on page: replace all | |||
markerLastAny.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " | |||
+ "Adding all markers to LastAny"); | |||
} | |||
public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) { | |||
if (pageMarkers == null) { | |||
pageMarkers = new Markers(); | |||
} | |||
pageMarkers.register(marks, starting, isfirst, islast); | |||
} | |||
/** | |||
* Get a marker from this page. | |||
* Resolve a marker from this page. | |||
* This will retrieve a marker with the class name | |||
* and position. | |||
* | |||
@@ -468,64 +374,17 @@ public class PageViewport extends AreaTreeObject implements Resolvable { | |||
* @param pos the position to retrieve | |||
* @return Object the marker found or null | |||
*/ | |||
public Marker getMarker(String name, int pos) { | |||
Marker mark = null; | |||
String posName = null; | |||
switch (pos) { | |||
case EN_FSWP: | |||
if (markerFirstStart != null) { | |||
mark = markerFirstStart.get(name); | |||
posName = "FSWP"; | |||
} | |||
if (mark == null && markerFirstAny != null) { | |||
mark = markerFirstAny.get(name); | |||
posName = "FirstAny after " + posName; | |||
} | |||
break; | |||
case EN_FIC: | |||
if (markerFirstAny != null) { | |||
mark = markerFirstAny.get(name); | |||
posName = "FIC"; | |||
} | |||
break; | |||
case EN_LSWP: | |||
if (markerLastStart != null) { | |||
mark = markerLastStart.get(name); | |||
posName = "LSWP"; | |||
} | |||
if (mark == null && markerLastAny != null) { | |||
mark = markerLastAny.get(name); | |||
posName = "LastAny after " + posName; | |||
} | |||
break; | |||
case EN_LEWP: | |||
if (markerLastEnd != null) { | |||
mark = markerLastEnd.get(name); | |||
posName = "LEWP"; | |||
} | |||
if (mark == null && markerLastAny != null) { | |||
mark = markerLastAny.get(name); | |||
posName = "LastAny after " + posName; | |||
} | |||
break; | |||
default: | |||
assert false; | |||
} | |||
if (log.isTraceEnabled()) { | |||
log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name | |||
+ " at position " + posName); | |||
public Marker resolveMarker(AbstractRetrieveMarker rm) { | |||
if (pageMarkers == null) { | |||
return null; | |||
} | |||
return mark; | |||
return pageMarkers.resolve(rm); | |||
} | |||
/** Dumps the current marker data to the logger. */ | |||
public void dumpMarkers() { | |||
if (log.isTraceEnabled()) { | |||
log.trace("FirstAny: " + this.markerFirstAny); | |||
log.trace("FirstStart: " + this.markerFirstStart); | |||
log.trace("LastAny: " + this.markerLastAny); | |||
log.trace("LastEnd: " + this.markerLastEnd); | |||
log.trace("LastStart: " + this.markerLastStart); | |||
if (pageMarkers != null) { | |||
pageMarkers.dump(); | |||
} | |||
} | |||
@@ -421,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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -807,8 +807,11 @@ public interface Constants { | |||
*/ | |||
int PR_X_NUMBER_CONVERSION_FEATURES = 285; | |||
/** Scope for table header */ | |||
int PR_X_HEADER_COLUMN = 286; | |||
/** Number of property constants defined */ | |||
int PROPERTY_COUNT = 285; | |||
int PROPERTY_COUNT = 286; | |||
// compound property constants | |||
@@ -1246,6 +1249,8 @@ public interface Constants { | |||
int EN_BT = 204; // bottom to top | |||
/** Enumeration constant */ | |||
int EN_TB_LR = 205; // for top-to-bottom, left-to-right writing mode | |||
/** Enumeration constant -- for fo:retrieve-table-marker */ | |||
int EN_FIRST_INCLUDING_CARRYOVER = 206; | |||
/** Number of enumeration constants defined */ | |||
int ENUM_COUNT = 205; | |||
int ENUM_COUNT = 206; | |||
} |
@@ -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; | |||
@@ -561,6 +562,19 @@ public abstract class FONode implements Cloneable { | |||
getFOValidationEventProducer().invalidChild(this, parentName, qn, ruleViolated, loc); | |||
} | |||
/** | |||
* Helper function to return "not supported child" exceptions. Note that the child is valid, just not | |||
* supported yet by FOP. | |||
* | |||
* @param loc org.xml.sax.Locator object of the error (*not* parent node) | |||
* @param nsURI namespace URI of incoming invalid node | |||
* @param lName local name (i.e., no prefix) of incoming node | |||
* @throws ValidationException the validation error provoked by the method call | |||
*/ | |||
protected void notSupportedChildError(Locator loc, String nsURI, String lName) throws ValidationException { | |||
getFOValidationEventProducer().notSupportedChild(this, getName(), new QName(nsURI, lName), loc); | |||
} | |||
/** | |||
* Helper function to throw an error caused by missing mandatory child elements. | |||
* (e.g., <code>fo:layout-master-set</code> not having any <code>fo:page-master</code> | |||
@@ -601,6 +615,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; | |||
@@ -2187,7 +2188,8 @@ public final class FOPropertyMapping implements Constants { | |||
m = new EnumProperty.Maker(PR_RETRIEVE_POSITION_WITHIN_TABLE); | |||
m.setInherited(false); | |||
m.addEnum("first-starting", getEnumProperty(EN_FIRST_STARTING, "FIRST_STARTING")); | |||
m.addEnum("first-including-carryover", getEnumProperty(EN_FIC, "FIC")); | |||
m.addEnum("first-including-carryover", | |||
getEnumProperty(EN_FIRST_INCLUDING_CARRYOVER, "FIRST_INCLUDING_CARRYOVER")); | |||
m.addEnum("last-starting", getEnumProperty(EN_LAST_STARTING, "LAST_STARTING")); | |||
m.addEnum("last-ending", getEnumProperty(EN_LAST_ENDING, "LAST_ENDING")); | |||
m.setDefault("first-starting"); | |||
@@ -2198,7 +2200,7 @@ public final class FOPropertyMapping implements Constants { | |||
m.setInherited(false); | |||
m.addEnum("table", getEnumProperty(EN_TABLE, "TABLE")); | |||
m.addEnum("table-fragment", getEnumProperty(EN_TABLE_FRAGMENT, "TABLE_FRAGMENT")); | |||
m.addEnum("page", getEnumProperty(EN_DOCUMENT, "PAGE")); | |||
m.addEnum("page", getEnumProperty(EN_PAGE, "PAGE")); | |||
m.setDefault("table"); | |||
addPropertyMaker("retrieve-boundary-within-table", m); | |||
} | |||
@@ -2277,7 +2279,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); | |||
@@ -2522,6 +2524,12 @@ public final class FOPropertyMapping implements Constants { | |||
m.setInherited(false); | |||
m.setDefault("false"); | |||
addPropertyMaker("table-omit-header-at-break", m); | |||
// fox:scope | |||
m = new EnumProperty.Maker(PR_X_HEADER_COLUMN); | |||
m.useGeneric(genericBoolean); | |||
m.setDefault("false"); | |||
addPropertyMaker("fox:header", m); | |||
} | |||
private void createWritingModeProperties() { |
@@ -89,6 +89,18 @@ public interface FOValidationEventProducer extends EventProducer { | |||
void invalidChild(Object source, String elementName, QName offendingNode, String ruleViolated, | |||
Locator loc) throws ValidationException; | |||
/** | |||
* A valid but not yet supported child was encountered. | |||
* | |||
* @param source the event source | |||
* @param elementName the name of the context node | |||
* @param offendingNode the offending node | |||
* @param loc the location of the error or null | |||
* @throws ValidationException the validation error provoked by the method call | |||
*/ | |||
void notSupportedChild(Object source, String elementName, QName offendingNode, Locator loc) | |||
throws ValidationException; | |||
/** | |||
* A required child element is missing. | |||
* @param source the event source |
@@ -17,6 +17,7 @@ | |||
<message key="tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message> | |||
<message key="nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message> | |||
<message key="invalidChild">"{offendingNode}" is not a valid child of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message> | |||
<message key="notSupportedChild">"{offendingNode}" as a child of "{elementName}" is not supported yet!{{locator}}</message> | |||
<message key="missingChildElement">"{elementName}" is missing child elements.[ Required content model: {contentModel}]{{locator}}</message> | |||
<message key="missingProperty">Element "{elementName}" is missing required property "{propertyName}"!{{locator}}</message> | |||
<message key="idNotUnique">Property ID "{id}" (found on "{elementName}") previously used; ID values must be unique within a document!{severity,equals,EventSeverity:FATAL,, Any reference to it will be considered a reference to the first occurrence in the document.}{{locator}}</message> |
@@ -20,6 +20,7 @@ | |||
package org.apache.fop.fo; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
@@ -35,6 +36,7 @@ import org.apache.xmlgraphics.util.QName; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.fo.extensions.ExtensionAttachment; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.table.TableCell; | |||
import org.apache.fop.fo.properties.PropertyMaker; | |||
/** | |||
@@ -65,7 +67,7 @@ public abstract class FObj extends FONode implements Constants { | |||
private boolean isOutOfLineFODescendant = false; | |||
/** Markers added to this element. */ | |||
private Map markers = null; | |||
private Map<String, Marker> markers; | |||
private int bidiLevel = -1; | |||
@@ -356,7 +358,7 @@ public abstract class FObj extends FONode implements Constants { | |||
} | |||
} | |||
if (markers == null) { | |||
markers = new java.util.HashMap(); | |||
markers = new HashMap<String, Marker>(); | |||
} | |||
if (!markers.containsKey(mcname)) { | |||
markers.put(mcname, marker); | |||
@@ -376,7 +378,7 @@ public abstract class FObj extends FONode implements Constants { | |||
/** | |||
* @return the collection of Markers attached to this object | |||
*/ | |||
public Map getMarkers() { | |||
public Map<String, Marker> getMarkers() { | |||
return markers; | |||
} | |||
@@ -522,6 +524,11 @@ public abstract class FObj extends FONode implements Constants { | |||
int found = 1; | |||
FONode temp = getParent(); | |||
while (temp != null) { | |||
if (temp instanceof TableCell && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) { | |||
// note that if the retrieve-table-marker is not in a table-header/footer an exception is | |||
// thrown, so no need to reset this flag in that case | |||
((TableCell) temp).flagAsHavingRetrieveTableMarker(); | |||
} | |||
if (temp.getNameId() == ancestorID) { | |||
return found; | |||
} |
@@ -52,6 +52,7 @@ public class ExtensionElementMapping extends ElementMapping { | |||
PROPERTY_ATTRIBUTES.add("disable-column-balancing"); | |||
//These are FOP's extension properties for accessibility | |||
PROPERTY_ATTRIBUTES.add("alt-text"); | |||
PROPERTY_ATTRIBUTES.add("header"); | |||
//fox:border-*-radius-* | |||
PROPERTY_ATTRIBUTES.add("border-before-radius-start"); | |||
PROPERTY_ATTRIBUTES.add("border-before-radius-end"); |
@@ -43,12 +43,15 @@ public class InternalElementMapping extends ElementMapping { | |||
/** The "struct-ref" attribute, to refer to a structure tree element. */ | |||
public static final String STRUCT_REF = "struct-ref"; | |||
public static final String SCOPE = "scope"; | |||
private static final Set<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>(); | |||
static { | |||
//These are FOP's extension properties for accessibility | |||
PROPERTY_ATTRIBUTES.add(STRUCT_ID); | |||
PROPERTY_ATTRIBUTES.add(STRUCT_REF); | |||
PROPERTY_ATTRIBUTES.add(SCOPE); | |||
} | |||
/** |
@@ -46,6 +46,11 @@ public abstract class AbstractRetrieveMarker extends FObjMixed { | |||
private String retrieveClassName; | |||
private int position; | |||
private String positionLabel; | |||
private int boundary; | |||
private String boundaryLabel; | |||
/** | |||
* Create a new AbstractRetrieveMarker instance that | |||
* is a child of the given {@link FONode} | |||
@@ -206,4 +211,43 @@ public abstract class AbstractRetrieveMarker extends FObjMixed { | |||
return this.retrieveClassName; | |||
} | |||
protected void setBoundaryLabel(String label) { | |||
this.boundaryLabel = label; | |||
} | |||
protected void setPositionLabel(String label) { | |||
this.positionLabel = label; | |||
} | |||
public String getBoundaryLabel() { | |||
return this.boundaryLabel; | |||
} | |||
public String getPositionLabel() { | |||
return this.positionLabel; | |||
} | |||
protected void setPosition(int position) { | |||
this.position = position; | |||
} | |||
protected void setBoundary(int boundary) { | |||
this.boundary = boundary; | |||
} | |||
public int getPosition() { | |||
return this.position; | |||
} | |||
public int getBoundary() { | |||
return this.boundary; | |||
} | |||
public abstract String getLocalName(); | |||
public abstract int getNameId(); | |||
public void changePositionTo(int position) { | |||
this.position = position; | |||
} | |||
} |
@@ -0,0 +1,212 @@ | |||
/* | |||
* 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.flow; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fo.Constants; | |||
/** | |||
* A class to register and resolve markers. | |||
*/ | |||
public final class Markers { | |||
// IsAny means either IsFirst or IsLast | |||
private Map<String, Marker> firstQualifyingIsFirst; | |||
private Map<String, Marker> firstQualifyingIsAny; | |||
private Map<String, Marker> lastQualifyingIsFirst; | |||
private Map<String, Marker> lastQualifyingIsLast; | |||
private Map<String, Marker> lastQualifyingIsAny; | |||
private static Log log = LogFactory.getLog(Markers.class); | |||
/** | |||
* Registers a marker with the position traits set. | |||
* Only the required markers are kept. | |||
* For "first-starting-within-page" it adds the markers | |||
* that are starting only if the marker class name is not | |||
* already added. | |||
* For "first-including-carryover" it adds any starting marker | |||
* if the marker class name is not already added. | |||
* For "last-starting-within-page" it adds all marks that | |||
* are starting, replacing earlier markers. | |||
* For "last-ending-within-page" it adds all markers that | |||
* are ending, replacing earlier markers. | |||
* | |||
* @param marks a map of markers to register | |||
* @param starting whether the registration happens at the start (true) or end (false) the the area | |||
* @param isfirst whether it is the first area of the parent LM | |||
* @param islast whether it is the last area of the parent LM | |||
*/ | |||
public void register(Map<String, Marker> marks, boolean starting, boolean isfirst, boolean islast) { | |||
// TODO: find way to put the page number in the log tracing | |||
if (marks == null) { | |||
return; | |||
} | |||
if (log.isDebugEnabled()) { | |||
log.debug("--" + marks.keySet() + ": " + (starting ? "starting" : "ending") | |||
+ (isfirst ? ", first" : "") + (islast ? ", last" : "")); | |||
} | |||
if (starting) { | |||
// at the start of the area, register is-first and any areas | |||
if (firstQualifyingIsAny == null) { | |||
firstQualifyingIsAny = new HashMap<String, Marker>(); | |||
} | |||
if (isfirst) { | |||
if (firstQualifyingIsFirst == null) { | |||
firstQualifyingIsFirst = new HashMap<String, Marker>(); | |||
} | |||
// first on scope: only put in new values, leave current | |||
for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) { | |||
String key = iter.next(); | |||
if (!firstQualifyingIsFirst.containsKey(key)) { | |||
firstQualifyingIsFirst.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding marker " + key + " to firstQualifyingIsFirst"); | |||
} | |||
} | |||
if (!firstQualifyingIsAny.containsKey(key)) { | |||
firstQualifyingIsAny.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding marker " + key + " to firstQualifyingIsAny"); | |||
} | |||
} | |||
} | |||
if (lastQualifyingIsFirst == null) { | |||
lastQualifyingIsFirst = new HashMap<String, Marker>(); | |||
} | |||
// last on scope: replace all | |||
lastQualifyingIsFirst.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding all markers to LastStart"); | |||
} | |||
} else { | |||
// first on scope: only put in new values, leave current | |||
for (Iterator<String> iter = marks.keySet().iterator(); iter.hasNext();) { | |||
String key = iter.next(); | |||
if (!firstQualifyingIsAny.containsKey(key)) { | |||
firstQualifyingIsAny.put(key, marks.get(key)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding marker " + key + " to firstQualifyingIsAny"); | |||
} | |||
} | |||
} | |||
} | |||
} else { | |||
// at the end of the area, register is-last and any areas | |||
if (islast) { | |||
if (lastQualifyingIsLast == null) { | |||
lastQualifyingIsLast = new HashMap<String, Marker>(); | |||
} | |||
// last on page: replace all | |||
lastQualifyingIsLast.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding all markers to lastQualifyingIsLast"); | |||
} | |||
} | |||
if (lastQualifyingIsAny == null) { | |||
lastQualifyingIsAny = new HashMap<String, Marker>(); | |||
} | |||
// last on page: replace all | |||
lastQualifyingIsAny.putAll(marks); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Adding all markers to lastQualifyingIsAny"); | |||
} | |||
} | |||
} | |||
/** | |||
* Retrieves the best candidate marker for the given position. | |||
* @param name the key used to register the marker | |||
* @param pos the retrieval scope position | |||
* @return a Marker instance | |||
*/ | |||
public Marker resolve(AbstractRetrieveMarker arm) { | |||
Marker mark = null; | |||
int pos = arm.getPosition(); | |||
String name = arm.getRetrieveClassName(); | |||
String posName = arm.getPositionLabel(); | |||
String localName = arm.getLocalName(); | |||
switch (pos) { | |||
case Constants.EN_FSWP: // retrieve-marker | |||
case Constants.EN_FIRST_STARTING: // retrieve-table-marker | |||
if (firstQualifyingIsFirst != null) { | |||
mark = firstQualifyingIsFirst.get(name); | |||
} | |||
if (mark == null && firstQualifyingIsAny != null) { | |||
mark = firstQualifyingIsAny.get(name); | |||
posName = "FirstAny after " + posName; | |||
} | |||
break; | |||
case Constants.EN_FIC: // retrieve-marker | |||
case Constants.EN_FIRST_INCLUDING_CARRYOVER: // retrieve-table-marker | |||
if (firstQualifyingIsAny != null) { | |||
mark = firstQualifyingIsAny.get(name); | |||
} | |||
break; | |||
case Constants.EN_LSWP: // retrieve-marker | |||
case Constants.EN_LAST_STARTING: // retrieve-table-marker | |||
if (lastQualifyingIsFirst != null) { | |||
mark = lastQualifyingIsFirst.get(name); | |||
} | |||
if (mark == null && lastQualifyingIsAny != null) { | |||
mark = lastQualifyingIsAny.get(name); | |||
posName = "LastAny after " + posName; | |||
} | |||
break; | |||
case Constants.EN_LEWP: // retrieve-marker | |||
case Constants.EN_LAST_ENDING: // retrieve-table-marker | |||
if (lastQualifyingIsLast != null) { | |||
mark = lastQualifyingIsLast.get(name); | |||
} | |||
if (mark == null && lastQualifyingIsAny != null) { | |||
mark = lastQualifyingIsAny.get(name); | |||
posName = "LastAny after " + posName; | |||
} | |||
break; | |||
default: | |||
throw new RuntimeException("Invalid position attribute in " + localName + "."); | |||
} | |||
if (log.isTraceEnabled()) { | |||
// TODO: find way to put the page number here | |||
log.trace(localName + ": name[" + name + "]; position [" + posName + "]"); | |||
} | |||
return mark; | |||
} | |||
/** Dumps the current marker data to the logger. */ | |||
public void dump() { | |||
if (log.isTraceEnabled()) { | |||
log.trace("FirstAny: " + this.firstQualifyingIsAny); | |||
log.trace("FirstStart: " + this.firstQualifyingIsFirst); | |||
log.trace("LastAny: " + this.lastQualifyingIsAny); | |||
log.trace("LastEnd: " + this.lastQualifyingIsLast); | |||
log.trace("LastStart: " + this.lastQualifyingIsFirst); | |||
} | |||
} | |||
} |
@@ -34,11 +34,6 @@ import org.apache.fop.fo.PropertyList; | |||
*/ | |||
public class RetrieveMarker extends AbstractRetrieveMarker { | |||
// The value of properties relevant for fo:retrieve-marker. | |||
private int retrievePosition; | |||
private int retrieveBoundary; | |||
// End of property values | |||
/** | |||
* Create a new RetrieveMarker instance that is a | |||
* child of the given {@link FONode}. | |||
@@ -70,8 +65,10 @@ public class RetrieveMarker extends AbstractRetrieveMarker { | |||
/** {@inheritDoc} */ | |||
public void bind(PropertyList pList) throws FOPException { | |||
super.bind(pList); | |||
this.retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum(); | |||
this.retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum(); | |||
setPosition(pList.get(PR_RETRIEVE_POSITION).getEnum()); | |||
setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION).getObject()); | |||
setBoundary(pList.get(PR_RETRIEVE_BOUNDARY).getEnum()); | |||
setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY).getObject()); | |||
} | |||
/** | |||
@@ -84,19 +81,19 @@ public class RetrieveMarker extends AbstractRetrieveMarker { | |||
* {@link org.apache.fop.fo.Constants#EN_LEWP}. | |||
*/ | |||
public int getRetrievePosition() { | |||
return this.retrievePosition; | |||
return getPosition(); | |||
} | |||
/** | |||
* Return the value for the <code>retrieve-boundary</code> | |||
* property | |||
* @return the value for retrieve-boundary-within-table; one of | |||
* @return the value for retrieve-boundary; one of | |||
* {@link org.apache.fop.fo.Constants#EN_PAGE}, | |||
* {@link org.apache.fop.fo.Constants#EN_PAGE_SEQUENCE}, | |||
* {@link org.apache.fop.fo.Constants#EN_DOCUMENT}. | |||
*/ | |||
public int getRetrieveBoundary() { | |||
return this.retrieveBoundary; | |||
return getBoundary(); | |||
} | |||
/** {@inheritDoc} */ |
@@ -32,11 +32,6 @@ import org.apache.fop.fo.PropertyList; | |||
*/ | |||
public class RetrieveTableMarker extends AbstractRetrieveMarker { | |||
// The value of properties relevant for fo:retrieve-table-marker. | |||
private int retrievePositionWithinTable; | |||
private int retrieveBoundaryWithinTable; | |||
// end property values | |||
/** | |||
* Create a new RetrieveTableMarker instance that is | |||
* a child of the given {@link FONode}. | |||
@@ -67,10 +62,10 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { | |||
/** {@inheritDoc} */ | |||
public void bind(PropertyList pList) throws FOPException { | |||
super.bind(pList); | |||
this.retrievePositionWithinTable | |||
= pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum(); | |||
this.retrieveBoundaryWithinTable | |||
= pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum(); | |||
setPosition(pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum()); | |||
setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getObject()); | |||
setBoundary(pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum()); | |||
setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getObject()); | |||
} | |||
/** | |||
@@ -83,7 +78,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { | |||
* {@link org.apache.fop.fo.Constants#EN_LAST_ENDING}. | |||
*/ | |||
public int getRetrievePositionWithinTable() { | |||
return this.retrievePositionWithinTable; | |||
return getPosition(); | |||
} | |||
/** | |||
@@ -95,7 +90,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { | |||
* {@link org.apache.fop.fo.Constants#EN_PAGE}. | |||
*/ | |||
public int getRetrieveBoundaryWithinTable() { | |||
return this.retrieveBoundaryWithinTable; | |||
return getBoundary(); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -110,4 +105,12 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { | |||
public int getNameId() { | |||
return FO_RETRIEVE_TABLE_MARKER; | |||
} | |||
/** {@inheritDoc} */ | |||
public void clearChildNodes() { | |||
super.clearChildNodes(); | |||
this.currentTextNode = null; | |||
this.lastFOTextProcessed = null; | |||
} | |||
} |
@@ -62,6 +62,8 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder { | |||
/** used for FO validation */ | |||
private boolean blockItemFound = false; | |||
private boolean hasRetrieveTableMarker; | |||
/** | |||
* Create a TableCell instance with the given {@link FONode} | |||
* as parent. | |||
@@ -247,4 +249,11 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder { | |||
return FO_TABLE_CELL; | |||
} | |||
public void flagAsHavingRetrieveTableMarker() { | |||
hasRetrieveTableMarker = true; | |||
} | |||
public boolean hasRetrieveTableMarker() { | |||
return hasRetrieveTableMarker; | |||
} | |||
} |
@@ -24,6 +24,7 @@ import org.xml.sax.Locator; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.datatypes.Length; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.ValidationException; | |||
@@ -44,6 +45,7 @@ public class TableColumn extends TableFObj { | |||
private Length columnWidth; | |||
private int numberColumnsRepeated; | |||
private int numberColumnsSpanned; | |||
private boolean isHeader; | |||
// Unused but valid items, commented out for performance: | |||
// private int visibility; | |||
// End of property values | |||
@@ -120,6 +122,7 @@ public class TableColumn extends TableFObj { | |||
if (!this.implicitColumn) { | |||
this.pList = pList; | |||
} | |||
isHeader = (pList.get(Constants.PR_X_HEADER_COLUMN).getEnum() == Constants.EN_TRUE); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -263,4 +266,13 @@ public class TableColumn extends TableFObj { | |||
this.pList = null; | |||
} | |||
/** | |||
* Returns {@code true} if this column is made of header cells. | |||
* | |||
* @return {@code true} if cells in this column are like TH cells in HTML | |||
*/ | |||
public boolean isHeader() { | |||
return isHeader; | |||
} | |||
} |
@@ -169,6 +169,8 @@ public abstract class TablePart extends TableCellContainer { | |||
getUserAgent().getEventBroadcaster()); | |||
eventProducer.noMixRowsAndCells(this, getName(), getLocator()); | |||
} | |||
} else if (localName.equals("retrieve-table-marker")) { | |||
notSupportedChildError(loc, nsURI, localName); | |||
} else { | |||
invalidChildError(loc, nsURI, localName); | |||
} |
@@ -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 index 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; | |||
} | |||
} |
@@ -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; | |||
@@ -30,9 +29,10 @@ import org.apache.avalon.framework.configuration.ConfigurationException; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.substitute.FontSubstitutions; | |||
import org.apache.fop.fonts.substitute.FontSubstitutionsConfigurator; | |||
@@ -72,6 +72,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 +92,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 { |
@@ -45,6 +45,7 @@ import org.apache.xmlgraphics.image.loader.ImageSize; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; | |||
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; | |||
import org.apache.xmlgraphics.image.loader.util.ImageUtil; | |||
import org.apache.xmlgraphics.io.XmlSourceUtil; | |||
import org.apache.xmlgraphics.util.MimeConstants; | |||
import org.apache.xmlgraphics.util.UnitConv; | |||
@@ -79,7 +80,7 @@ public class PreloaderSVG extends AbstractImagePreloader { | |||
} | |||
} | |||
if (info != null) { | |||
ImageUtil.closeQuietly(src); //Image is fully read | |||
XmlSourceUtil.closeQuietly(src); //Image is fully read | |||
} | |||
return info; | |||
} | |||
@@ -119,7 +120,7 @@ public class PreloaderSVG extends AbstractImagePreloader { | |||
DOMSource domSrc = (DOMSource)src; | |||
doc = (SVGDocument)domSrc.getNode(); | |||
} else { | |||
in = new UnclosableInputStream(ImageUtil.needInputStream(src)); | |||
in = new UnclosableInputStream(XmlSourceUtil.needInputStream(src)); | |||
int length = in.available(); | |||
in.mark(length + 1); | |||
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( |
@@ -38,6 +38,7 @@ import org.apache.xmlgraphics.image.loader.ImageInfo; | |||
import org.apache.xmlgraphics.image.loader.ImageSize; | |||
import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; | |||
import org.apache.xmlgraphics.image.loader.util.ImageUtil; | |||
import org.apache.xmlgraphics.io.XmlSourceUtil; | |||
import org.apache.fop.util.UnclosableInputStream; | |||
@@ -69,7 +70,7 @@ public class PreloaderWMF extends AbstractImagePreloader { | |||
} | |||
} | |||
if (info != null) { | |||
ImageUtil.closeQuietly(src); //Image is fully read | |||
XmlSourceUtil.closeQuietly(src); //Image is fully read | |||
} | |||
return info; | |||
} | |||
@@ -88,7 +89,7 @@ public class PreloaderWMF extends AbstractImagePreloader { | |||
ImageContext context) { | |||
// parse document and get the size attributes of the svg element | |||
InputStream in = new UnclosableInputStream(ImageUtil.needInputStream(src)); | |||
InputStream in = new UnclosableInputStream(XmlSourceUtil.needInputStream(src)); | |||
try { | |||
in.mark(4 + 1); | |||
@@ -273,4 +273,11 @@ public abstract class AbstractBaseLayoutManager | |||
throw new UnsupportedOperationException("Not implemented"); | |||
} | |||
public void preserveChildrenAtEndOfLayout() { | |||
} | |||
public void recreateChildrenLMs() { | |||
} | |||
} |
@@ -37,6 +37,7 @@ import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.RetrieveMarker; | |||
import org.apache.fop.layoutmgr.table.TableLayoutManager; | |||
/** | |||
* The base class for most LayoutManagers. | |||
@@ -67,6 +68,8 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im | |||
private int lastGeneratedPosition = -1; | |||
private int smallestPosNumberChecked = Integer.MAX_VALUE; | |||
private boolean preserveChildrenAtEndOfLayout; | |||
/** | |||
* Abstract layout manager. | |||
*/ | |||
@@ -370,19 +373,20 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im | |||
} | |||
/** | |||
* Registers the FO's markers on the current PageViewport | |||
* Registers the FO's markers on the current PageViewport, and if applicable on the parent TableLM. | |||
* | |||
* @param isStarting boolean indicating whether the markers qualify as 'starting' | |||
* @param isFirst boolean indicating whether the markers qualify as 'first' | |||
* @param isLast boolean indicating whether the markers qualify as 'last' | |||
*/ | |||
protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) { | |||
protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) { | |||
if (this.markers != null) { | |||
getCurrentPV().addMarkers( | |||
getCurrentPV().registerMarkers( | |||
this.markers, | |||
isStarting, | |||
isFirst, | |||
isLast); | |||
possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); | |||
} | |||
} | |||
@@ -419,11 +423,12 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im | |||
notifyEndOfLayout(); | |||
/* References to the child LMs are no longer needed | |||
*/ | |||
childLMs = null; | |||
curChildLM = null; | |||
childLMiter = null; | |||
if (!preserveChildrenAtEndOfLayout) { | |||
// References to the child LMs are no longer needed | |||
childLMs = null; | |||
curChildLM = null; | |||
childLMiter = null; | |||
} | |||
/* markers that qualify have been transferred to the page | |||
*/ | |||
@@ -438,13 +443,21 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im | |||
|| lm instanceof PageSequenceLayoutManager)) { | |||
lm = lm.getParent(); | |||
} | |||
if (lm instanceof FlowLayoutManager) { | |||
if (lm instanceof FlowLayoutManager && !preserveChildrenAtEndOfLayout) { | |||
fobj.clearChildNodes(); | |||
fobjIter = null; | |||
} | |||
} | |||
} | |||
/* | |||
* Preserves the children LMs at the end of layout. This is necessary if the layout is expected to be | |||
* repeated, as when using retrieve-table-markers. | |||
*/ | |||
public void preserveChildrenAtEndOfLayout() { | |||
preserveChildrenAtEndOfLayout = true; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public String toString() { | |||
@@ -467,4 +480,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im | |||
lastGeneratedPosition = -1; | |||
} | |||
public void recreateChildrenLMs() { | |||
childLMs = new ArrayList(); | |||
isFinished = false; | |||
if (fobj == null) { | |||
return; | |||
} | |||
fobjIter = fobj.getChildNodes(); | |||
int position = 0; | |||
while (createNextChildLMs(position++)) { | |||
// | |||
} | |||
childLMiter = new LMiter(this); | |||
for (LMiter iter = new LMiter(this); iter.hasNext();) { | |||
AbstractBaseLayoutManager alm = (AbstractBaseLayoutManager) iter.next(); | |||
alm.initialize(); | |||
alm.recreateChildrenLMs(); | |||
alm.preserveChildrenAtEndOfLayout(); | |||
} | |||
curChildLM = getChildLM(); | |||
} | |||
protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting, | |||
boolean isFirst, boolean isLast) { | |||
LayoutManager lm = this.parentLayoutManager; | |||
if (lm instanceof FlowLayoutManager || lm instanceof PageSequenceLayoutManager | |||
|| !(lm instanceof AbstractLayoutManager)) { | |||
return; | |||
} | |||
((AbstractLayoutManager) lm).possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); | |||
} | |||
} |
@@ -226,15 +226,14 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa | |||
public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) { | |||
AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); | |||
String name = rm.getRetrieveClassName(); | |||
int pos = rm.getRetrievePosition(); | |||
int boundary = rm.getRetrieveBoundary(); | |||
// get marker from the current markers on area tree | |||
Marker mark = (Marker)getCurrentPV().getMarker(name, pos); | |||
Marker mark = getCurrentPV().resolveMarker(rm); | |||
if (mark == null && boundary != EN_PAGE) { | |||
// go back over pages until mark found | |||
// if document boundary then keep going | |||
boolean doc = boundary == EN_DOCUMENT; | |||
boolean doc = (boundary == EN_DOCUMENT); | |||
int seq = areaTreeModel.getPageSequenceCount(); | |||
int page = areaTreeModel.getPageCount(seq) - 1; | |||
while (page < 0 && doc && seq > 1) { | |||
@@ -243,7 +242,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa | |||
} | |||
while (page >= 0) { | |||
PageViewport pv = areaTreeModel.getPage(seq, page); | |||
mark = (Marker)pv.getMarker(name, Constants.EN_LEWP); | |||
int originalPosition = rm.getPosition(); | |||
rm.changePositionTo(Constants.EN_LEWP); | |||
mark = (Marker) pv.resolveMarker(rm); | |||
// this is probably not necessary since the RM will not be used again, but to be safe... | |||
rm.changePositionTo(originalPosition); | |||
if (mark != null) { | |||
break; | |||
} |
@@ -87,7 +87,7 @@ public final class AreaAdditionUtil { | |||
} | |||
if (bslm != null) { | |||
bslm.addMarkersToPage( | |||
bslm.registerMarkers( | |||
true, | |||
bslm.isFirst(firstPos), | |||
bslm.isLast(lastPos)); | |||
@@ -114,11 +114,10 @@ public final class AreaAdditionUtil { | |||
} | |||
if (bslm != null) { | |||
bslm.addMarkersToPage( | |||
bslm.registerMarkers( | |||
false, | |||
bslm.isFirst(firstPos), | |||
bslm.isLast(lastPos)); | |||
bslm.checkEndOfLayout(lastPos); | |||
} | |||
@@ -806,7 +806,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl | |||
addId(); | |||
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(true, isFirst(firstPos), isLast(lastPos)); | |||
if (bcpos == null) { | |||
// the Positions in positionList were inside the elements | |||
@@ -826,7 +826,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl | |||
bcpos.getBreaker().addContainedAreas(layoutContext); | |||
} | |||
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(false, isFirst(firstPos), isLast(lastPos)); | |||
TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(), | |||
effSpaceBefore, effSpaceAfter); |
@@ -312,7 +312,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co | |||
addId(); | |||
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(true, isFirst(firstPos), isLast(lastPos)); | |||
// the Positions in positionList were inside the elements | |||
// created by the LineLM | |||
@@ -327,7 +327,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co | |||
childLM.addAreas(childPosIter, lc); | |||
} | |||
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(false, isFirst(firstPos), isLast(lastPos)); | |||
TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), | |||
effSpaceBefore, effSpaceAfter); |
@@ -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 */ |
@@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.area.AreaTreeHandler; | |||
import org.apache.fop.fo.FOElementMapping; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FONode.FONodeIterator; | |||
import org.apache.fop.fo.FOText; | |||
import org.apache.fop.fo.FObjMixed; | |||
import org.apache.fop.fo.extensions.ExternalDocument; | |||
@@ -117,7 +118,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
registerMaker(Block.class, new BlockLayoutManagerMaker()); | |||
registerMaker(Leader.class, new LeaderLayoutManagerMaker()); | |||
registerMaker(RetrieveMarker.class, new RetrieveMarkerLayoutManagerMaker()); | |||
registerMaker(RetrieveTableMarker.class, new Maker()); | |||
registerMaker(RetrieveTableMarker.class, new RetrieveTableMarkerLayoutManagerMaker()); | |||
registerMaker(Character.class, new CharacterLayoutManagerMaker()); | |||
registerMaker(ExternalGraphic.class, | |||
new ExternalGraphicLayoutManagerMaker()); | |||
@@ -407,6 +408,24 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
} | |||
} | |||
public class RetrieveTableMarkerLayoutManagerMaker extends Maker { | |||
public void make(FONode node, List lms) { | |||
FONodeIterator baseIter = node.getChildNodes(); | |||
if (baseIter == null) { | |||
// this happens when the retrieve-table-marker cannot be resolved yet | |||
RetrieveTableMarker rtm = (RetrieveTableMarker) node; | |||
RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm); | |||
lms.add(rtmlm); | |||
return; | |||
} | |||
while (baseIter.hasNext()) { | |||
// this happens when the retrieve-table-marker has been resolved | |||
FONode child = (FONode) baseIter.next(); | |||
makeLayoutManagers(child, lms); | |||
} | |||
} | |||
} | |||
/** a layout manager maker */ | |||
public class WrapperLayoutManagerMaker extends Maker { | |||
/** {@inheritDoc} */ |
@@ -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.layoutmgr; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; | |||
import org.apache.fop.layoutmgr.inline.TextLayoutManager; | |||
public abstract class LocalBreaker extends AbstractBreaker { | |||
protected BlockStackingLayoutManager lm; | |||
private int displayAlign; | |||
private int ipd; | |||
private int overflow = 0; | |||
public LocalBreaker(BlockStackingLayoutManager lm, int ipd, int displayAlign) { | |||
this.lm = lm; | |||
this.ipd = ipd; | |||
this.displayAlign = displayAlign; | |||
} | |||
/** {@inheritDoc} */ | |||
protected boolean isPartOverflowRecoveryActivated() { | |||
// For side regions, this must be disabled because of wanted overflow. | |||
return false; | |||
} | |||
public boolean isOverflow() { | |||
return (this.overflow != 0); | |||
} | |||
public int getOverflowAmount() { | |||
return this.overflow; | |||
} | |||
/** {@inheritDoc} */ | |||
protected PageBreakingLayoutListener createLayoutListener() { | |||
return new PageBreakingLayoutListener() { | |||
public void notifyOverflow(int part, int amount, FObj obj) { | |||
if (LocalBreaker.this.overflow == 0) { | |||
LocalBreaker.this.overflow = amount; | |||
} | |||
} | |||
}; | |||
} | |||
protected LayoutManager getTopLevelLM() { | |||
return lm; | |||
} | |||
protected LayoutContext createLayoutContext() { | |||
LayoutContext lc = super.createLayoutContext(); | |||
lc.setRefIPD(ipd); | |||
return lc; | |||
} | |||
protected List getNextKnuthElements(LayoutContext context, int alignment) { | |||
LayoutManager curLM; // currently active LM | |||
List returnList = new LinkedList(); | |||
while ((curLM = lm.getChildLM()) != null) { | |||
LayoutContext childLC = LayoutContext.newInstance(); | |||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||
childLC.setRefIPD(context.getRefIPD()); | |||
childLC.setWritingMode(context.getWritingMode()); | |||
List returnedList = null; | |||
// The following is a HACK! Ignore leading and trailing white space | |||
boolean ignore = curLM instanceof TextLayoutManager; | |||
if (!curLM.isFinished()) { | |||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||
} | |||
if (returnedList != null && !ignore) { | |||
lm.wrapPositionElements(returnedList, returnList); | |||
} | |||
} | |||
SpaceResolver.resolveElementList(returnList); | |||
lm.setFinished(true); | |||
return returnList; | |||
} | |||
protected int getCurrentDisplayAlign() { | |||
return displayAlign; | |||
} | |||
protected boolean hasMoreContent() { | |||
return !lm.isFinished(); | |||
} | |||
protected void addAreas(PositionIterator posIter, LayoutContext context) { | |||
AreaAdditionUtil.addAreas(lm, posIter, context); | |||
} | |||
protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList, | |||
BlockSequence effectiveList) { | |||
if (partCount > 1) { | |||
PageBreakPosition pos = (PageBreakPosition) alg.getPageBreaks().getFirst(); | |||
int firstPartLength = ElementListUtils.calcContentLength(effectiveList, | |||
effectiveList.ignoreAtStart, pos.getLeafPos()); | |||
overflow += alg.totalWidth - firstPartLength; | |||
} | |||
// Rendering all parts (not just the first) at once for the case where the parts that | |||
// overflow should be visible. | |||
alg.removeAllPageBreaks(); | |||
// Directly add areas after finding the breaks | |||
this.addAreas(alg, 1, originalList, effectiveList); | |||
} | |||
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { | |||
// nop for static content | |||
} | |||
protected LayoutManager getCurrentChildLM() { | |||
return null; // TODO NYI | |||
} | |||
} |
@@ -0,0 +1,75 @@ | |||
/* | |||
* 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.layoutmgr; | |||
import java.util.List; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.flow.RetrieveTableMarker; | |||
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; | |||
import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager; | |||
import org.apache.fop.layoutmgr.table.TableLayoutManager; | |||
public class RetrieveTableMarkerLayoutManager extends LeafNodeLayoutManager { | |||
private static Log log = LogFactory.getLog(RetrieveTableMarkerLayoutManager.class); | |||
public RetrieveTableMarkerLayoutManager(RetrieveTableMarker node) { | |||
super(node); | |||
} | |||
/** {@inheritDoc} */ | |||
public List getNextKnuthElements(LayoutContext context, int alignment) { | |||
setFinished(true); | |||
FONode foNode = (FONode) getFObj(); | |||
foNode = getTableLayoutManager().resolveRetrieveTableMarker((RetrieveTableMarker) foNode); | |||
if (foNode != null) { | |||
// resolve the RTM and replace current LM by the resolved target LM | |||
InlineLevelLayoutManager illm = (InlineLevelLayoutManager) getPSLM().getLayoutManagerMaker() | |||
.makeLayoutManager(foNode); | |||
if (illm instanceof RetrieveTableMarkerLayoutManager) { | |||
// happens if the retrieve-marker was empty | |||
return null; | |||
} | |||
illm.setParent(getParent()); | |||
illm.initialize(); | |||
return illm.getNextKnuthElements(context, alignment); | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public void addAreas(PositionIterator posIter, LayoutContext context) { | |||
} | |||
private TableLayoutManager getTableLayoutManager() { | |||
LayoutManager parentLM = getParent(); | |||
while (!(parentLM instanceof TableLayoutManager)) { | |||
parentLM = parentLM.getParent(); | |||
} | |||
TableLayoutManager tlm = (TableLayoutManager) parentLM; | |||
return tlm; | |||
} | |||
} |
@@ -167,125 +167,22 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
return (StaticContent) fobj; | |||
} | |||
private class StaticContentBreaker extends AbstractBreaker { | |||
private StaticContentLayoutManager lm; | |||
private int displayAlign; | |||
private int ipd; | |||
private int overflow = 0; | |||
public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, | |||
int displayAlign) { | |||
this.lm = lm; | |||
this.ipd = ipd; | |||
this.displayAlign = displayAlign; | |||
private class StaticContentBreaker extends LocalBreaker { | |||
public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, int displayAlign) { | |||
super(lm, ipd, displayAlign); | |||
} | |||
/** {@inheritDoc} */ | |||
protected void observeElementList(List elementList) { | |||
String elementListID = getStaticContentFO().getFlowName(); | |||
String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId(); | |||
String pageSequenceID = ((PageSequence) lm.getParent().getFObj()).getId(); | |||
if (pageSequenceID != null && pageSequenceID.length() > 0) { | |||
elementListID += "-" + pageSequenceID; | |||
} | |||
ElementListObserver.observe(elementList, "static-content", elementListID); | |||
} | |||
/** {@inheritDoc} */ | |||
protected boolean isPartOverflowRecoveryActivated() { | |||
//For side regions, this must be disabled because of wanted overflow. | |||
return false; | |||
} | |||
public boolean isOverflow() { | |||
return (this.overflow != 0); | |||
} | |||
public int getOverflowAmount() { | |||
return this.overflow; | |||
} | |||
/** {@inheritDoc} */ | |||
protected PageBreakingLayoutListener createLayoutListener() { | |||
return new PageBreakingLayoutListener() { | |||
public void notifyOverflow(int part, int amount, FObj obj) { | |||
if (StaticContentBreaker.this.overflow == 0) { | |||
StaticContentBreaker.this.overflow = amount; | |||
} | |||
} | |||
}; | |||
} | |||
protected LayoutManager getTopLevelLM() { | |||
return lm; | |||
} | |||
protected LayoutContext createLayoutContext() { | |||
LayoutContext lc = super.createLayoutContext(); | |||
lc.setRefIPD(ipd); | |||
return lc; | |||
} | |||
protected List getNextKnuthElements(LayoutContext context, int alignment) { | |||
LayoutManager curLM; // currently active LM | |||
List returnList = new LinkedList(); | |||
while ((curLM = getChildLM()) != null) { | |||
LayoutContext childLC = LayoutContext.newInstance(); | |||
childLC.setStackLimitBP(context.getStackLimitBP()); | |||
childLC.setRefIPD(context.getRefIPD()); | |||
childLC.setWritingMode(context.getWritingMode()); | |||
List returnedList = null; | |||
//The following is a HACK! Ignore leading and trailing white space | |||
boolean ignore = curLM instanceof TextLayoutManager; | |||
if (!curLM.isFinished()) { | |||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||
} | |||
if (returnedList != null && !ignore) { | |||
lm.wrapPositionElements(returnedList, returnList); | |||
} | |||
} | |||
SpaceResolver.resolveElementList(returnList); | |||
setFinished(true); | |||
return returnList; | |||
} | |||
protected int getCurrentDisplayAlign() { | |||
return displayAlign; | |||
} | |||
protected boolean hasMoreContent() { | |||
return !lm.isFinished(); | |||
} | |||
protected void addAreas(PositionIterator posIter, LayoutContext context) { | |||
AreaAdditionUtil.addAreas(lm, posIter, context); | |||
} | |||
protected void doPhase3(PageBreakingAlgorithm alg, int partCount, | |||
BlockSequence originalList, BlockSequence effectiveList) { | |||
if (partCount > 1) { | |||
PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst(); | |||
int firstPartLength = ElementListUtils.calcContentLength(effectiveList, | |||
effectiveList.ignoreAtStart, pos.getLeafPos()); | |||
overflow += alg.totalWidth - firstPartLength; | |||
} | |||
//Rendering all parts (not just the first) at once for the case where the parts that | |||
//overflow should be visible. | |||
alg.removeAllPageBreaks(); | |||
//Directly add areas after finding the breaks | |||
this.addAreas(alg, 1, originalList, effectiveList); | |||
} | |||
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { | |||
//nop for static content | |||
} | |||
protected LayoutManager getCurrentChildLM() { | |||
return null; //TODO NYI | |||
} | |||
} | |||
/** |
@@ -480,7 +480,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { | |||
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this)); | |||
} | |||
addMarkersToPage( | |||
registerMarkers( | |||
true, | |||
!areaCreated, | |||
lastPos == null || isLast(lastPos)); | |||
@@ -542,7 +542,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { | |||
setTraits(areaCreated, lastPos == null || !isLast(lastPos)); | |||
parentLayoutManager.addChildArea(getCurrentArea()); | |||
addMarkersToPage( | |||
registerMarkers( | |||
false, | |||
!areaCreated, | |||
lastPos == null || isLast(lastPos)); |
@@ -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()) { |
@@ -171,7 +171,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager | |||
} | |||
} | |||
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(true, isFirst(firstPos), isLast(lastPos)); | |||
PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); | |||
while ((childLM = childPosIter.getNextChildLM()) != null) { | |||
@@ -184,7 +184,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager | |||
childLM.addAreas(childPosIter, lc); | |||
} | |||
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(false, isFirst(firstPos), isLast(lastPos)); | |||
// We are done with this area add the background | |||
TraitSetter.addBackground(curBlockArea, |
@@ -136,7 +136,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp | |||
} | |||
} | |||
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(true, isFirst(firstPos), isLast(lastPos)); | |||
PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); | |||
while ((childLM = childPosIter.getNextChildLM()) != null) { | |||
@@ -149,7 +149,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp | |||
childLM.addAreas(childPosIter, lc); | |||
} | |||
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(false, isFirst(firstPos), isLast(lastPos)); | |||
flush(); | |||
@@ -509,7 +509,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements | |||
} | |||
} | |||
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(true, isFirst(firstPos), isLast(lastPos)); | |||
// use the first and the last ListItemPosition to determine the | |||
// corresponding indexes in the original labelList and bodyList | |||
@@ -563,7 +563,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements | |||
} | |||
curBlockArea.setBPD(itemBPD); | |||
addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); | |||
registerMarkers(false, isFirst(firstPos), isLast(lastPos)); | |||
// We are done with this area add the background | |||
TraitSetter.addBackground(curBlockArea, |
@@ -303,11 +303,16 @@ class RowPainter { | |||
borderAfterWhich = ConditionalBorder.REST; | |||
} | |||
// when adding the areas for the TableCellLayoutManager this helps with the isLast trait | |||
// if, say, the first cell of a row has content that fits in the page, but the content of | |||
// the second cell does not fit this will assure that the isLast trait for the first cell | |||
// will also be false | |||
lastCellParts[i].pgu.getCellLM().setLastTrait(lastCellParts[i].isLastPart()); | |||
addAreasForCell(firstCellParts[i].pgu, | |||
firstCellParts[i].start, lastCellParts[i].end, | |||
actualRowHeight, borderBeforeWhich, borderAfterWhich, | |||
lastOnPage); | |||
firstCellParts[i] = null; | |||
firstCellParts[i] = null; // why? what about the lastCellParts[i]? | |||
Arrays.fill(firstCellOnPage, i, i + currentGU.getCell().getNumberColumnsSpanned(), | |||
false); | |||
} |
@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
@@ -28,19 +29,24 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.area.Area; | |||
import org.apache.fop.area.Block; | |||
import org.apache.fop.area.Trait; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.table.ConditionalBorder; | |||
import org.apache.fop.fo.flow.table.GridUnit; | |||
import org.apache.fop.fo.flow.table.PrimaryGridUnit; | |||
import org.apache.fop.fo.flow.table.Table; | |||
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.TablePart; | |||
import org.apache.fop.fo.flow.table.TableRow; | |||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground; | |||
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; | |||
import org.apache.fop.layoutmgr.AbstractLayoutManager; | |||
import org.apache.fop.layoutmgr.AreaAdditionUtil; | |||
import org.apache.fop.layoutmgr.BlockLevelLayoutManager; | |||
import org.apache.fop.layoutmgr.BlockStackingLayoutManager; | |||
import org.apache.fop.layoutmgr.ElementListObserver; | |||
import org.apache.fop.layoutmgr.ElementListUtils; | |||
import org.apache.fop.layoutmgr.Keep; | |||
import org.apache.fop.layoutmgr.KnuthBox; | |||
@@ -49,8 +55,10 @@ import org.apache.fop.layoutmgr.KnuthGlue; | |||
import org.apache.fop.layoutmgr.KnuthPenalty; | |||
import org.apache.fop.layoutmgr.LayoutContext; | |||
import org.apache.fop.layoutmgr.LayoutManager; | |||
import org.apache.fop.layoutmgr.LocalBreaker; | |||
import org.apache.fop.layoutmgr.Position; | |||
import org.apache.fop.layoutmgr.PositionIterator; | |||
import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager; | |||
import org.apache.fop.layoutmgr.SpaceResolver; | |||
import org.apache.fop.layoutmgr.TraitSetter; | |||
import org.apache.fop.traits.BorderProps; | |||
@@ -79,6 +87,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
private int totalHeight; | |||
private int usedBPD; | |||
private boolean emptyCell = true; | |||
private boolean isDescendantOfTableFooter; | |||
private boolean isDescendantOfTableHeader; | |||
private boolean hasRetrieveTableMarker; | |||
// place holder for the addAreas arguments | |||
private boolean savedAddAreasArguments; | |||
private PositionIterator savedParentIter; | |||
private LayoutContext savedLayoutContext; | |||
private int[] savedSpannedGridRowHeights; | |||
private int savedStartRow; | |||
private int savedEndRow; | |||
private int savedBorderBeforeWhich; | |||
private int savedBorderAfterWhich; | |||
private boolean savedFirstOnPage; | |||
private boolean savedLastOnPage; | |||
private RowPainter savedPainter; | |||
private int savedFirstRowHeight; | |||
// this is set to false when the table-cell has a retrieve-table-marker and is in the table-header | |||
private boolean flushArea = true; | |||
// this information is set by the RowPainter | |||
private boolean isLastTrait; | |||
/** | |||
* Create a new Cell layout manager. | |||
@@ -88,6 +118,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) { | |||
super(node); | |||
this.primaryGridUnit = pgu; | |||
this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader | |||
|| node.getParent() instanceof TableHeader; | |||
this.isDescendantOfTableFooter = node.getParent().getParent() instanceof TableFooter | |||
|| node.getParent() instanceof TableFooter; | |||
this.hasRetrieveTableMarker = node.hasRetrieveTableMarker(); | |||
} | |||
/** @return the table-cell FO */ | |||
@@ -248,6 +283,84 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
totalHeight = h; | |||
} | |||
private void clearRetrieveTableMarkerChildNodes(List<LayoutManager> childrenLMs) { | |||
if (childrenLMs == null) { | |||
return; | |||
} | |||
int n = childrenLMs.size(); | |||
for (int j = 0; j < n; j++) { | |||
LayoutManager lm = (LayoutManager) childrenLMs.get(j); | |||
if (lm == null) { | |||
return; | |||
} else if (lm instanceof RetrieveTableMarkerLayoutManager) { | |||
((AbstractLayoutManager) lm).getFObj().clearChildNodes(); | |||
} else { | |||
List<LayoutManager> lms = lm.getChildLMs(); | |||
clearRetrieveTableMarkerChildNodes(lms); | |||
} | |||
} | |||
} | |||
/** | |||
* Checks whether the associated table cell of this LM is in a table header or footer. | |||
* @return true if descendant of table header or footer | |||
*/ | |||
private boolean isDescendantOfTableHeaderOrFooter() { | |||
return (isDescendantOfTableFooter || isDescendantOfTableHeader); | |||
} | |||
private void saveAddAreasArguments(PositionIterator parentIter, LayoutContext layoutContext, | |||
int[] spannedGridRowHeights, int startRow, int endRow, int borderBeforeWhich, | |||
int borderAfterWhich, boolean firstOnPage, boolean lastOnPage, RowPainter painter, | |||
int firstRowHeight) { | |||
// checks for savedAddAreasArguments and isDescendantOfTableHeader were already made but repeat them | |||
if (savedAddAreasArguments) { | |||
return; | |||
} | |||
if (isDescendantOfTableHeader) { | |||
savedAddAreasArguments = true; | |||
savedParentIter = null /* parentIter */; | |||
savedLayoutContext = null /* layoutContext */; | |||
savedSpannedGridRowHeights = spannedGridRowHeights; | |||
savedStartRow = startRow; | |||
savedEndRow = endRow; | |||
savedBorderBeforeWhich = borderBeforeWhich; | |||
savedBorderAfterWhich = borderAfterWhich; | |||
savedFirstOnPage = firstOnPage; | |||
savedLastOnPage = lastOnPage; | |||
savedPainter = painter; | |||
savedFirstRowHeight = firstRowHeight; | |||
TableLayoutManager parentTableLayoutManager = getTableLayoutManager(); | |||
parentTableLayoutManager.saveTableHeaderTableCellLayoutManagers(this); | |||
// this saving is done the first time the addArea() is called; since the retrieve-table-markers | |||
// cannot be resolved at this time we do not want to flush the area; the area needs nevertheless | |||
// be built so that space is allocated for it. | |||
flushArea = false; | |||
} | |||
} | |||
private TableLayoutManager getTableLayoutManager() { | |||
LayoutManager parentLM = getParent(); | |||
while (!(parentLM instanceof TableLayoutManager)) { | |||
parentLM = parentLM.getParent(); | |||
} | |||
TableLayoutManager tlm = (TableLayoutManager) parentLM; | |||
return tlm; | |||
} | |||
/** | |||
* Calls the addAreas() using the original arguments. | |||
*/ | |||
protected void repeatAddAreas() { | |||
if (savedAddAreasArguments) { | |||
addAreas(savedParentIter, savedLayoutContext, savedSpannedGridRowHeights, savedStartRow, | |||
savedEndRow, savedBorderBeforeWhich, savedBorderAfterWhich, savedFirstOnPage, | |||
savedLastOnPage, savedPainter, savedFirstRowHeight); | |||
// so that the arguments of the next table fragment header can be saved | |||
savedAddAreasArguments = false; | |||
} | |||
} | |||
/** | |||
* Add the areas for the break points. The cell contains block stacking layout | |||
* managers that add block areas. | |||
@@ -407,7 +520,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
} | |||
} | |||
AreaAdditionUtil.addAreas(this, parentIter, layoutContext); | |||
if (isDescendantOfTableHeaderOrFooter()) { | |||
if (hasRetrieveTableMarker) { | |||
if (isDescendantOfTableHeader && !savedAddAreasArguments) { | |||
saveAddAreasArguments(parentIter, layoutContext, spannedGridRowHeights, startRow, endRow, | |||
borderBeforeWhich, borderAfterWhich, firstOnPage, lastOnPage, painter, | |||
firstRowHeight); | |||
} | |||
recreateChildrenLMs(); | |||
int displayAlign = ((TableCell) this.getFObj()).getDisplayAlign(); | |||
TableCellBreaker breaker = new TableCellBreaker(this, cellIPD, displayAlign); | |||
breaker.doLayout(usedBPD, false); | |||
// this is needed so the next time the LMs are recreated they look like the originals; this | |||
// is due to the fact that during the doLayout() above the FO tree changes when the | |||
// retrieve-table-markers are resolved | |||
clearRetrieveTableMarkerChildNodes(getChildLMs()); | |||
} | |||
} | |||
// if hasRetrieveTableMarker == true the areas were already added when the re-layout was done above | |||
if (!hasRetrieveTableMarker) { | |||
AreaAdditionUtil.addAreas(this, parentIter, layoutContext); | |||
} | |||
// Re-adjust the cell's bpd as it may have been modified by the previous call | |||
// for some reason (?) | |||
curBlockArea.setBPD(cellBPD); | |||
@@ -418,7 +552,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
getTableCell().getCommonBorderPaddingBackground(), this); | |||
} | |||
flush(); | |||
if (flushArea) { | |||
flush(); | |||
} else { | |||
flushArea = true; | |||
} | |||
curBlockArea = null; | |||
@@ -604,4 +742,49 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager | |||
return true; | |||
} | |||
private class TableCellBreaker extends LocalBreaker { | |||
public TableCellBreaker(TableCellLayoutManager lm, int ipd, int displayAlign) { | |||
super(lm, ipd, displayAlign); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
protected void observeElementList(List elementList) { | |||
String elementListID = lm.getParent().getFObj().getId() + "-" + lm.getFObj().getId(); | |||
ElementListObserver.observe(elementList, "table-cell", elementListID); | |||
} | |||
} | |||
/** | |||
* Registers the FO's markers on the current PageViewport and parent Table. | |||
* | |||
* @param isStarting boolean indicating whether the markers qualify as 'starting' | |||
* @param isFirst boolean indicating whether the markers qualify as 'first' | |||
* @param isLast boolean indicating whether the markers qualify as 'last' | |||
*/ | |||
protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) { | |||
Map<String, Marker> markers = getTableCell().getMarkers(); | |||
if (markers != null) { | |||
getCurrentPV().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait); | |||
if (!isDescendantOfTableHeaderOrFooter()) { | |||
getTableLayoutManager().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait); | |||
} | |||
} | |||
} | |||
void setLastTrait(boolean isLast) { | |||
isLastTrait = isLast; | |||
} | |||
/** {@inheritDoc} */ | |||
public void setParent(LayoutManager lm) { | |||
this.parentLayoutManager = lm; | |||
if (this.hasRetrieveTableMarker) { | |||
this.getTableLayoutManager().flagAsHavingRetrieveTableMarker(); | |||
} | |||
} | |||
} |
@@ -31,9 +31,11 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.datatypes.PercentBaseContext; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.table.EffRow; | |||
import org.apache.fop.fo.flow.table.PrimaryGridUnit; | |||
import org.apache.fop.fo.flow.table.Table; | |||
import org.apache.fop.fo.flow.table.TableBody; | |||
import org.apache.fop.fo.flow.table.TablePart; | |||
import org.apache.fop.layoutmgr.BreakElement; | |||
import org.apache.fop.layoutmgr.ElementListUtils; | |||
@@ -400,9 +402,13 @@ public class TableContentLayoutManager implements PercentBaseContext { | |||
} | |||
} | |||
Map markers = getTableLM().getTable().getMarkers(); | |||
// there may be table fragment markers stored; clear them since we are starting a new fragment | |||
tableLM.clearTableFragmentMarkers(); | |||
// note: markers at table level are to be retrieved by the page, not by the table itself | |||
Map<String, Marker> markers = getTableLM().getTable().getMarkers(); | |||
if (markers != null) { | |||
getTableLM().getCurrentPV().addMarkers(markers, | |||
getTableLM().getCurrentPV().registerMarkers(markers, | |||
true, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos)); | |||
} | |||
@@ -430,6 +436,10 @@ public class TableContentLayoutManager implements PercentBaseContext { | |||
addBodyAreas(tablePositions.iterator(), painter, footerElements == null); | |||
} | |||
// if there are TCLMs saved because they have a RetrieveTableMarker, we repeat the header areas now; | |||
// this can also be done after the areas for the footer are added but should be the same as here | |||
tableLM.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers(); | |||
if (footerElements != null) { | |||
boolean ancestorTreatAsArtifact = layoutContext.treatAsArtifact(); | |||
layoutContext.setTreatAsArtifact(treatFooterAsArtifact); | |||
@@ -442,7 +452,7 @@ public class TableContentLayoutManager implements PercentBaseContext { | |||
this.usedBPD += painter.getAccumulatedBPD(); | |||
if (markers != null) { | |||
getTableLM().getCurrentPV().addMarkers(markers, | |||
getTableLM().getCurrentPV().registerMarkers(markers, | |||
false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos)); | |||
} | |||
} | |||
@@ -503,14 +513,20 @@ public class TableContentLayoutManager implements PercentBaseContext { | |||
*/ | |||
private void addTablePartAreas(List positions, RowPainter painter, TablePart body, | |||
boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) { | |||
getTableLM().getCurrentPV().addMarkers(body.getMarkers(), | |||
getTableLM().getCurrentPV().registerMarkers(body.getMarkers(), | |||
true, isFirstPos, isLastPos); | |||
if (body instanceof TableBody) { | |||
getTableLM().registerMarkers(body.getMarkers(), true, isFirstPos, isLastPos); | |||
} | |||
painter.startTablePart(body); | |||
for (Iterator iter = positions.iterator(); iter.hasNext();) { | |||
painter.handleTableContentPosition((TableContentPosition) iter.next()); | |||
} | |||
getTableLM().getCurrentPV().addMarkers(body.getMarkers(), | |||
getTableLM().getCurrentPV().registerMarkers(body.getMarkers(), | |||
false, isFirstPos, isLastPos); | |||
if (body instanceof TableBody) { | |||
getTableLM().registerMarkers(body.getMarkers(), false, isFirstPos, isLastPos); | |||
} | |||
painter.endTablePart(lastInBody, lastOnPage); | |||
} | |||
@@ -23,6 +23,7 @@ import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
@@ -33,12 +34,16 @@ import org.apache.fop.datatypes.LengthBase; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.fo.FObj; | |||
import org.apache.fop.fo.flow.Marker; | |||
import org.apache.fop.fo.flow.Markers; | |||
import org.apache.fop.fo.flow.RetrieveTableMarker; | |||
import org.apache.fop.fo.flow.table.Table; | |||
import org.apache.fop.fo.flow.table.TableColumn; | |||
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 +67,7 @@ import org.apache.fop.util.BreakUtil; | |||
* the render background. | |||
*/ | |||
public class TableLayoutManager extends BlockStackingLayoutManager | |||
implements ConditionalElementListener { | |||
implements ConditionalElementListener, BreakOpportunity { | |||
/** | |||
* logging instance | |||
@@ -92,6 +97,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager | |||
private Position auxiliaryPosition; | |||
// this holds a possible list of TCLMs that needed to have their addAreas() repeated | |||
private List<TableCellLayoutManager> savedTCLMs; | |||
private boolean areAllTCLMsSaved; | |||
private Markers tableMarkers; | |||
private Markers tableFragmentMarkers; | |||
private boolean hasRetrieveTableMarker; | |||
/** | |||
* Temporary holder of column background informations for a table-cell's area. | |||
* | |||
@@ -559,4 +573,112 @@ public class TableLayoutManager extends BlockStackingLayoutManager | |||
tableUnit = 0.0; | |||
} | |||
/** | |||
* Saves a TableCellLayoutManager for later use. | |||
* | |||
* @param tclm a TableCellLayoutManager that has a RetrieveTableMarker | |||
*/ | |||
protected void saveTableHeaderTableCellLayoutManagers(TableCellLayoutManager tclm) { | |||
if (savedTCLMs == null) { | |||
savedTCLMs = new ArrayList<TableCellLayoutManager>(); | |||
} | |||
if (!areAllTCLMsSaved) { | |||
savedTCLMs.add(tclm); | |||
} | |||
} | |||
/** | |||
* Calls addAreas() for each of the saved TableCellLayoutManagers. | |||
*/ | |||
protected void repeatAddAreasForSavedTableHeaderTableCellLayoutManagers() { | |||
if (savedTCLMs == null) { | |||
return; | |||
} | |||
// if we get to this stage then we are at the footer of the table fragment; this means that no more | |||
// different TCLM need to be saved (we already have all); we flag the list as being complete then | |||
areAllTCLMsSaved = true; | |||
for (int i = 0; i < savedTCLMs.size(); i++) { | |||
TableCellLayoutManager tclm = savedTCLMs.get(i); | |||
tclm.repeatAddAreas(); | |||
} | |||
} | |||
/** | |||
* Resolves a RetrieveTableMarker by finding a qualifying Marker to which it is bound to. | |||
* @param rtm the RetrieveTableMarker to be resolved | |||
* @return a bound RetrieveTableMarker instance or null if no qualifying Marker found | |||
*/ | |||
public RetrieveTableMarker resolveRetrieveTableMarker(RetrieveTableMarker rtm) { | |||
String name = rtm.getRetrieveClassName(); | |||
int originalPosition = rtm.getPosition(); | |||
boolean changedPosition = false; | |||
Marker mark = null; | |||
// try the primary retrieve scope area, which is the same as table-fragment | |||
mark = (tableFragmentMarkers == null) ? null : tableFragmentMarkers.resolve(rtm); | |||
if (mark == null && rtm.getBoundary() != Constants.EN_TABLE_FRAGMENT) { | |||
rtm.changePositionTo(Constants.EN_LAST_ENDING); | |||
changedPosition = true; | |||
// try the page scope area | |||
mark = getCurrentPV().resolveMarker(rtm); | |||
if (mark == null && rtm.getBoundary() != Constants.EN_PAGE) { | |||
// try the table scope area | |||
mark = (tableMarkers == null) ? null : tableMarkers.resolve(rtm); | |||
} | |||
} | |||
if (changedPosition) { | |||
// so that the next time it is called looks unchanged | |||
rtm.changePositionTo(originalPosition); | |||
} | |||
if (mark == null) { | |||
log.debug("found no marker with name: " + name); | |||
return null; | |||
} else { | |||
rtm.bindMarker(mark); | |||
return rtm; | |||
} | |||
} | |||
/** | |||
* Register the markers for this table. | |||
* | |||
* @param marks the map of markers to add | |||
* @param starting if the area being added is starting or ending | |||
* @param isfirst if the area being added has is-first trait | |||
* @param islast if the area being added has is-last trait | |||
*/ | |||
public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst, | |||
boolean islast) { | |||
if (tableMarkers == null) { | |||
tableMarkers = new Markers(); | |||
} | |||
tableMarkers.register(marks, starting, isfirst, islast); | |||
if (tableFragmentMarkers == null) { | |||
tableFragmentMarkers = new Markers(); | |||
} | |||
tableFragmentMarkers.register(marks, starting, isfirst, islast); | |||
} | |||
/** | |||
* Clears the list of markers in the current table fragment. Should be called just before starting a new | |||
* header (that belongs to the next table fragment). | |||
*/ | |||
protected void clearTableFragmentMarkers() { | |||
tableFragmentMarkers = null; | |||
} | |||
public void flagAsHavingRetrieveTableMarker() { | |||
hasRetrieveTableMarker = true; | |||
} | |||
protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting, | |||
boolean isFirst, boolean isLast) { | |||
// note: if we allow table-footer after a table-body this check should not be made and the markers | |||
// should be registered regardless because the retrieval may be done only in the footer | |||
if (hasRetrieveTableMarker) { | |||
registerMarkers(markers, isStarting, isFirst, isLast); | |||
} | |||
super.possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); | |||
} | |||
} |
@@ -355,36 +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(StructureType structureType, PDFObject parent) { | |||
PDFStructElem structElem = new PDFStructElem(parent, structureType); | |||
public void registerStructureElement(PDFStructElem structElem) { | |||
assignObjectNumber(structElem); | |||
structureTreeElements.add(structElem); | |||
return structElem; | |||
} | |||
/** | |||
* 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 | |||
* @param scope the scope of the given table header element | |||
* @return a dictionary of type 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 PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent, | |||
Scope scope) { | |||
PDFStructElem structElem = makeStructureElement(structureType, parent); | |||
public void registerStructureElement(PDFStructElem structElem, Scope scope) { | |||
registerStructureElement(structElem); | |||
versionController.addTableHeaderScopeAttribute(structElem, scope); | |||
return structElem; | |||
} | |||
/** |
@@ -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)); | |||
} | |||
} |
@@ -32,7 +32,8 @@ 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 StructureType structureType; | |||
@@ -51,7 +52,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement | |||
* @param parent parent of this element | |||
* @param structureType the structure type of this element | |||
*/ | |||
PDFStructElem(PDFObject parent, StructureType structureType) { | |||
public PDFStructElem(PDFObject parent, StructureType structureType) { | |||
this(parent); | |||
this.structureType = structureType; | |||
put("S", structureType.getName()); | |||
@@ -86,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>(); |
@@ -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); | |||
} |
@@ -111,6 +111,11 @@ public final class StandardStructureTypes { | |||
return name; | |||
} | |||
@Override | |||
public String toString() { | |||
return name.toString().substring(1); | |||
} | |||
} | |||
private static final Map<String, StructureType> STRUCTURE_TYPES = new HashMap<String, StructureType>(); |
@@ -17,43 +17,21 @@ | |||
/* $Id$ */ | |||
package org.apache.fop.apps.io; | |||
import java.io.FilterInputStream; | |||
import java.io.InputStream; | |||
package org.apache.fop.pdf; | |||
/** | |||
* This class represents a resolved resource. The type property is used by FOP to identify the resource | |||
* content. | |||
* 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 Resource extends FilterInputStream { | |||
private final String type; | |||
/** | |||
* @param type resource type | |||
* @param inputStream input stream of the resource | |||
*/ | |||
public Resource(String type, InputStream inputStream) { | |||
super(inputStream); | |||
this.type = type; | |||
} | |||
public abstract class StructureHierarchyMember extends PDFDictionary { | |||
/** | |||
* Constructs a resource of 'unknown' type. | |||
* Adds the given object to the array of kids. | |||
* | |||
* @param inputStream input stream of the resource | |||
*/ | |||
public Resource(InputStream inputStream) { | |||
this("unknown", inputStream); | |||
} | |||
/** | |||
* @return the resource type | |||
* @param kid an object to be added to the K entry | |||
*/ | |||
public String getType() { | |||
return this.type; | |||
} | |||
public abstract void addKid(PDFObject kid); | |||
} |
@@ -328,7 +328,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( |