git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1737084 13f79535-47bb-0310-9956-ffa450edef68pull/3/head
@@ -159,4 +159,12 @@ public interface ResourceEventProducer extends EventProducer { | |||
* @event.severity WARN | |||
*/ | |||
void imageWarning(Object source, String message); | |||
/** | |||
* Hyphenation not found. | |||
* @param source the event source | |||
* @param uri the original URI of the hyphenation | |||
* @event.severity WARN | |||
*/ | |||
void hyphenationNotFound(Object source, String uri); | |||
} |
@@ -807,6 +807,9 @@ public class FOUserAgent { | |||
return factory.getHyphenationPatternNames(); | |||
} | |||
public InternalResourceResolver getHyphenationResourceResolver() { | |||
return factory.getHyphenationResourceResolver(); | |||
} | |||
public SoftMapCache getPDFObjectCache() { | |||
return pdfObjectCache; | |||
} |
@@ -45,6 +45,7 @@ import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.FontManagerConfigurator; | |||
import org.apache.fop.hyphenation.HyphenationTreeCache; | |||
import org.apache.fop.hyphenation.Hyphenator; | |||
import org.apache.fop.util.LogUtil; | |||
/** | |||
@@ -248,6 +249,7 @@ public class FopConfParser { | |||
false)); | |||
} | |||
setHyphenationBase(cfg, resourceResolver, baseURI, fopFactoryBuilder); | |||
setHyphPatNames(cfg, fopFactoryBuilder, strict); | |||
// prefer Renderer over IFDocumentHandler | |||
@@ -268,19 +270,38 @@ public class FopConfParser { | |||
configureImageLoading(cfg.getChild("image-loading", false), strict); | |||
} | |||
private void setHyphenationBase(Configuration cfg, ResourceResolver resourceResolver, URI baseURI, | |||
FopFactoryBuilder fopFactoryBuilder) throws FOPException { | |||
if (cfg.getChild("hyphenation-base", false) != null) { | |||
try { | |||
URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("hyphenation-base").getValue(null)); | |||
fopFactoryBuilder.setHyphenBaseResourceResolver( | |||
ResourceResolverFactory.createInternalResourceResolver( | |||
baseURI.resolve(fontBase), resourceResolver)); | |||
} catch (URISyntaxException use) { | |||
LogUtil.handleException(log, use, true); | |||
} | |||
} else { | |||
fopFactoryBuilder.setHyphenBaseResourceResolver( | |||
ResourceResolverFactory.createInternalResourceResolver( | |||
fopFactoryBuilder.getBaseURI(), resourceResolver)); | |||
} | |||
} | |||
private void setHyphPatNames(Configuration cfg, FopFactoryBuilder builder, boolean strict) | |||
throws FOPException { | |||
Configuration[] hyphPatConfig = cfg.getChildren("hyphenation-pattern"); | |||
if (hyphPatConfig.length != 0) { | |||
Map<String, String> hyphPatNames = new HashMap<String, String>(); | |||
for (int i = 0; i < hyphPatConfig.length; ++i) { | |||
for (Configuration aHyphPatConfig : hyphPatConfig) { | |||
String lang; | |||
String country; | |||
String filename; | |||
StringBuffer error = new StringBuffer(); | |||
String location = hyphPatConfig[i].getLocation(); | |||
String location = aHyphPatConfig.getLocation(); | |||
lang = hyphPatConfig[i].getAttribute("lang", null); | |||
lang = aHyphPatConfig.getAttribute("lang", null); | |||
if (lang == null) { | |||
addError("The lang attribute of a hyphenation-pattern configuration" | |||
+ " element must exist (" + location + ")", error); | |||
@@ -291,7 +312,7 @@ public class FopConfParser { | |||
} | |||
lang = lang.toLowerCase(Locale.getDefault()); | |||
country = hyphPatConfig[i].getAttribute("country", null); | |||
country = aHyphPatConfig.getAttribute("country", null); | |||
if ("".equals(country)) { | |||
country = null; | |||
} | |||
@@ -304,7 +325,7 @@ public class FopConfParser { | |||
country = country.toUpperCase(Locale.getDefault()); | |||
} | |||
filename = hyphPatConfig[i].getValue(null); | |||
filename = aHyphPatConfig.getValue(null); | |||
if (filename == null) { | |||
addError("The value of a hyphenation-pattern configuration" | |||
+ " element may not be empty (" + location + ")", error); | |||
@@ -316,7 +337,15 @@ public class FopConfParser { | |||
} | |||
String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); | |||
hyphPatNames.put(llccKey, filename); | |||
String type = aHyphPatConfig.getAttribute("type", null); | |||
if ("xml".equals(type)) { | |||
hyphPatNames.put(llccKey, filename + Hyphenator.XMLTYPE); | |||
} else if ("hyp".equals(type)) { | |||
hyphPatNames.put(llccKey, filename + Hyphenator.HYPTYPE); | |||
} else { | |||
hyphPatNames.put(llccKey, filename); | |||
} | |||
if (log.isDebugEnabled()) { | |||
log.debug("Using hyphenation pattern filename " + filename | |||
+ " for lang=\"" + lang + "\"" |
@@ -361,6 +361,10 @@ public final class FopFactory implements ImageContext { | |||
return config.getTargetResolution(); | |||
} | |||
public InternalResourceResolver getHyphenationResourceResolver() { | |||
return config.getHyphenationResourceResolver(); | |||
} | |||
/** | |||
* Returns the conversion factor from pixel units to millimeters. This | |||
* depends on the desired source resolution. |
@@ -33,6 +33,7 @@ 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.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.FontManager; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
@@ -183,6 +184,11 @@ public final class FopFactoryBuilder { | |||
return this; | |||
} | |||
public FopFactoryBuilder setHyphenBaseResourceResolver(InternalResourceResolver hyphenationResourceResolver) { | |||
fopFactoryConfigBuilder.setHyphenationResourceResolver(hyphenationResourceResolver); | |||
return this; | |||
} | |||
/** | |||
* Sets whether to perform strict validation on the FO used. | |||
* | |||
@@ -329,6 +335,8 @@ public final class FopFactoryBuilder { | |||
private URI baseURI; | |||
private InternalResourceResolver hyphenationResourceResolver; | |||
private boolean hasStrictFOValidation = true; | |||
private boolean hasStrictUserValidation = FopFactoryConfig.DEFAULT_STRICT_USERCONFIG_VALIDATION; | |||
@@ -393,6 +401,10 @@ public final class FopFactoryBuilder { | |||
return baseURI; | |||
} | |||
public InternalResourceResolver getHyphenationResourceResolver() { | |||
return hyphenationResourceResolver; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean validateStrictly() { | |||
return hasStrictFOValidation; | |||
@@ -479,6 +491,8 @@ public final class FopFactoryBuilder { | |||
void setBaseURI(URI baseURI); | |||
void setHyphenationResourceResolver(InternalResourceResolver hyphenationResourceResolver); | |||
void setStrictFOValidation(boolean validateStrictly); | |||
void setStrictUserConfigValidation(boolean validateStrictly); | |||
@@ -528,6 +542,10 @@ public final class FopFactoryBuilder { | |||
throwIllegalStateException(); | |||
} | |||
public void setHyphenationResourceResolver(InternalResourceResolver hyphenationResourceResolver) { | |||
throwIllegalStateException(); | |||
} | |||
public void setStrictFOValidation(boolean validateStrictly) { | |||
throwIllegalStateException(); | |||
} | |||
@@ -603,6 +621,10 @@ public final class FopFactoryBuilder { | |||
config.baseURI = baseURI; | |||
} | |||
public void setHyphenationResourceResolver(InternalResourceResolver hyphenationResourceResolver) { | |||
config.hyphenationResourceResolver = hyphenationResourceResolver; | |||
} | |||
public void setStrictFOValidation(boolean validateStrictly) { | |||
config.hasStrictFOValidation = validateStrictly; | |||
} |
@@ -29,6 +29,7 @@ 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.InternalResourceResolver; | |||
import org.apache.fop.fonts.FontManager; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
@@ -165,6 +166,8 @@ public interface FopFactoryConfig { | |||
/** @return the hyphenation pattern names */ | |||
Map<String, String> getHyphenationPatternNames(); | |||
InternalResourceResolver getHyphenationResourceResolver(); | |||
/** | |||
* Controls the mechanisms that are used in the event that {@link javax.xml.transform.Source} | |||
* used for resources couldn't be read. |
@@ -32,7 +32,9 @@ import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.ResourceEventProducer; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.events.EventBroadcaster; | |||
/** | |||
* <p>This class is the main entry point to the hyphenation package. | |||
@@ -50,6 +52,9 @@ public final class Hyphenator { | |||
/** Enables a dump of statistics. Note: If activated content is sent to System.out! */ | |||
private static boolean statisticsDump; | |||
public static final String HYPTYPE = Hyphenator.class.toString() + "HYP"; | |||
public static final String XMLTYPE = Hyphenator.class.toString() + "XML"; | |||
/** | |||
* Creates a new hyphenator. | |||
*/ | |||
@@ -81,8 +86,13 @@ public final class Hyphenator { | |||
* @param hyphPatNames the map with user-configured hyphenation pattern file names | |||
* @return the hyphenation tree | |||
*/ | |||
public static HyphenationTree getHyphenationTree(String lang, | |||
String country, InternalResourceResolver resolver, Map hyphPatNames) { | |||
public static HyphenationTree getHyphenationTree(String lang, String country, | |||
InternalResourceResolver resolver, Map hyphPatNames) { | |||
return getHyphenationTree(lang, country, resolver, hyphPatNames, null); | |||
} | |||
public static HyphenationTree getHyphenationTree(String lang, String country, | |||
InternalResourceResolver resourceResolver, Map hyphPatNames, EventBroadcaster eventBroadcaster) { | |||
String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); | |||
HyphenationTreeCache cache = getHyphenationTreeCache(); | |||
@@ -91,57 +101,6 @@ public final class Hyphenator { | |||
return null; | |||
} | |||
HyphenationTree hTree = getHyphenationTree2(lang, country, resolver, hyphPatNames); | |||
// fallback to lang only | |||
if (hTree == null && country != null && !country.equals("none")) { | |||
String llKey = HyphenationTreeCache.constructLlccKey(lang, null); | |||
if (!cache.isMissing(llKey)) { | |||
hTree = getHyphenationTree2(lang, null, resolver, hyphPatNames); | |||
if (hTree != null && log.isDebugEnabled()) { | |||
log.debug("Couldn't find hyphenation pattern " | |||
+ "for lang=\"" + lang + "\",country=\"" + country + "\"." | |||
+ " Using general language pattern " | |||
+ "for lang=\"" + lang + "\" instead."); | |||
} | |||
if (hTree == null) { | |||
// no fallback; register as missing | |||
cache.noteMissing(llKey); | |||
} else { | |||
// also register for (lang,country) | |||
cache.cache(llccKey, hTree); | |||
} | |||
} | |||
} | |||
if (hTree == null) { | |||
// (lang,country) and (lang) tried; register as missing | |||
cache.noteMissing(llccKey); | |||
log.error("Couldn't find hyphenation pattern " | |||
+ "for lang=\"" + lang + "\"" | |||
+ (country != null && !country.equals("none") | |||
? ",country=\"" + country + "\"" | |||
: "") | |||
+ "."); | |||
} | |||
return hTree; | |||
} | |||
/** | |||
* Returns a hyphenation tree for a given language and country | |||
* The hyphenation trees are cached. | |||
* @param lang the language | |||
* @param country the country (may be null or "none") | |||
* @param resourceResolver resolver to find the hyphenation files | |||
* @param hyphPatNames the map with user-configured hyphenation pattern file names | |||
* @return the hyphenation tree | |||
*/ | |||
public static HyphenationTree getHyphenationTree2(String lang, | |||
String country, InternalResourceResolver resourceResolver, Map hyphPatNames) { | |||
String llccKey = HyphenationTreeCache.constructLlccKey(lang, country); | |||
HyphenationTreeCache cache = getHyphenationTreeCache(); | |||
HyphenationTree hTree; | |||
// first try to find it in the cache | |||
hTree = getHyphenationTreeCache().getHyphenationTree(lang, country); | |||
@@ -153,7 +112,6 @@ public final class Hyphenator { | |||
if (key == null) { | |||
key = llccKey; | |||
} | |||
if (resourceResolver != null) { | |||
hTree = getUserHyphenationTree(key, resourceResolver); | |||
} | |||
@@ -161,11 +119,23 @@ public final class Hyphenator { | |||
hTree = getFopHyphenationTree(key); | |||
} | |||
if (hTree == null && country != null && !country.equals("none")) { | |||
return getHyphenationTree(lang, null, resourceResolver, hyphPatNames, eventBroadcaster); | |||
} | |||
// put it into the pattern cache | |||
if (hTree != null) { | |||
cache.cache(llccKey, hTree); | |||
} else { | |||
if (eventBroadcaster == null) { | |||
log.error("Couldn't find hyphenation pattern " + llccKey); | |||
} else { | |||
ResourceEventProducer producer = ResourceEventProducer.Provider.get(eventBroadcaster); | |||
String name = key.replace(HYPTYPE, "").replace(XMLTYPE, ""); | |||
producer.hyphenationNotFound(cache, name); | |||
} | |||
cache.noteMissing(llccKey); | |||
} | |||
return hTree; | |||
} | |||
@@ -254,22 +224,30 @@ public final class Hyphenator { | |||
// first try serialized object | |||
String name = key + ".hyp"; | |||
try { | |||
InputStream in = getHyphenationTreeStream(name, resourceResolver); | |||
if (key.endsWith(HYPTYPE)) { | |||
name = key.replace(HYPTYPE, ""); | |||
} | |||
if (!key.endsWith(XMLTYPE)) { | |||
try { | |||
hTree = readHyphenationTree(in); | |||
} finally { | |||
IOUtils.closeQuietly(in); | |||
} | |||
return hTree; | |||
} catch (IOException ioe) { | |||
if (log.isDebugEnabled()) { | |||
log.debug("I/O problem while trying to load " + name, ioe); | |||
InputStream in = getHyphenationTreeStream(name, resourceResolver); | |||
try { | |||
hTree = readHyphenationTree(in); | |||
} finally { | |||
IOUtils.closeQuietly(in); | |||
} | |||
return hTree; | |||
} catch (IOException ioe) { | |||
if (log.isDebugEnabled()) { | |||
log.debug("I/O problem while trying to load " + name, ioe); | |||
} | |||
} | |||
} | |||
// try the raw XML file | |||
name = key + ".xml"; | |||
if (key.endsWith(XMLTYPE)) { | |||
name = key.replace(XMLTYPE, ""); | |||
} | |||
hTree = new HyphenationTree(); | |||
try { | |||
InputStream in = getHyphenationTreeStream(name, resourceResolver); | |||
@@ -320,7 +298,13 @@ public final class Hyphenator { | |||
public static Hyphenation hyphenate(String lang, String country, | |||
InternalResourceResolver resourceResolver, Map hyphPatNames, String word, int leftMin, | |||
int rightMin) { | |||
HyphenationTree hTree = getHyphenationTree(lang, country, resourceResolver, hyphPatNames); | |||
return hyphenate(lang, country, resourceResolver, hyphPatNames, word, leftMin, rightMin, null); | |||
} | |||
public static Hyphenation hyphenate(String lang, String country, InternalResourceResolver resourceResolver, | |||
Map hyphPatNames, String word, int leftMin, int rightMin, | |||
EventBroadcaster eventBroadcaster) { | |||
HyphenationTree hTree = getHyphenationTree(lang, country, resourceResolver, hyphPatNames, eventBroadcaster); | |||
if (hTree == null) { | |||
return null; | |||
} |
@@ -1432,11 +1432,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager | |||
// on an inline or wrapper below the block level. | |||
Hyphenation hyph = Hyphenator.hyphenate(hyphenationProperties.language.getString(), | |||
hyphenationProperties.country.getString(), | |||
getFObj().getUserAgent().getResourceResolver(), | |||
getFObj().getUserAgent().getHyphenationResourceResolver(), | |||
getFObj().getUserAgent().getHyphenationPatternNames(), | |||
sbChars.toString(), | |||
hyphenationProperties.hyphenationRemainCharacterCount.getValue(), | |||
hyphenationProperties.hyphenationPushCharacterCount.getValue()); | |||
hyphenationProperties.hyphenationPushCharacterCount.getValue(), | |||
getFObj().getUserAgent().getEventBroadcaster()); | |||
// They hyph structure contains the information we need | |||
// Now start from prev: reset to that position, ask that LM to get | |||
// a Position for the first hyphenation offset. If the offset isn't in |
@@ -30,4 +30,5 @@ | |||
<message key="cannotDeleteTempFile">Temporary file could not be deleted: {tempFile}</message> | |||
<message key="catalogResolverNotFound">Catalog resolver not found along the classpath</message> | |||
<message key="catalogResolverNotCreated">Error creating the catalog resolver: {message}</message> | |||
<message key="hyphenationNotFound">Hyphenation pattern not found.[ URI: {uri}.]</message> | |||
</catalogue> |
@@ -0,0 +1,71 @@ | |||
/* | |||
* 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; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.ObjectOutputStream; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.hyphenation.Hyphenation; | |||
import org.apache.fop.hyphenation.HyphenationException; | |||
import org.apache.fop.hyphenation.HyphenationTree; | |||
import org.apache.fop.hyphenation.Hyphenator; | |||
public class HyphenationTestCase { | |||
@Test | |||
public void testHyphenator() { | |||
File f = new File("test/resources/fop"); | |||
InternalResourceResolver resourceResolver = ResourceResolverFactory.createDefaultInternalResourceResolver( | |||
f.toURI()); | |||
Hyphenation hyph = Hyphenator.hyphenate("fr.xml" + Hyphenator.XMLTYPE, null, resourceResolver, null, | |||
"hello", 0, 0); | |||
assertEquals(hyph.toString(), "-hel-lo"); | |||
} | |||
@Test | |||
public void testHyphenatorBinary() throws HyphenationException, IOException { | |||
File f = File.createTempFile("hyp", "fop"); | |||
f.delete(); | |||
f.mkdir(); | |||
InternalResourceResolver resourceResolver = ResourceResolverFactory.createDefaultInternalResourceResolver( | |||
f.toURI()); | |||
HyphenationTree hTree = new HyphenationTree(); | |||
hTree.loadPatterns(new File("test/resources/fop/fr.xml").getAbsolutePath()); | |||
File hyp = new File(f, "fr.hyp"); | |||
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(hyp)); | |||
out.writeObject(hTree); | |||
out.close(); | |||
Hyphenation hyph = Hyphenator.hyphenate("fr.hyp" + Hyphenator.HYPTYPE, null, resourceResolver, null, | |||
"oello", 0, 0); | |||
assertEquals(hyph.toString(), "oel-lo"); | |||
hyp.delete(); | |||
f.delete(); | |||
} | |||
} |
@@ -27,6 +27,7 @@ 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.InternalResourceResolver; | |||
import org.apache.fop.fonts.FontManager; | |||
import org.apache.fop.layoutmgr.LayoutManagerMaker; | |||
@@ -132,6 +133,10 @@ public final class MutableConfig implements FopFactoryConfig { | |||
return delegate.getHyphenationPatternNames(); | |||
} | |||
public InternalResourceResolver getHyphenationResourceResolver() { | |||
return delegate.getHyphenationResourceResolver(); | |||
} | |||
public FallbackResolver getFallbackResolver() { | |||
return delegate.getFallbackResolver(); | |||
} |
@@ -158,4 +158,12 @@ public class EventProcessingTestCase { | |||
doTest("region-body_overflow.fo", BlockLevelEventProducer.class.getName() + ".regionOverflow", | |||
params); | |||
} | |||
@Test | |||
public void testHyphenationNotFound() throws Exception { | |||
Map<String, Object> noParams = Collections.emptyMap(); | |||
doTest(BASE_DIR.resolve("hyphenation.fo").toURL().openStream(), | |||
new File("test/events/hyphenationfop.xconf").toURI(), | |||
ResourceEventProducer.class.getName() + ".hyphenationNotFound", MimeConstants.MIME_PDF, noParams); | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" hyphenate="true" language="fr"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="simple" page-height="27.9cm" page-width="21.6cm"> | |||
<fo:region-body /> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="simple"> | |||
<fo:flow flow-name="xsl-region-body"> | |||
<fo:block font-size="30pt">Text should have hyphenation:</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<fop version="1.0"> | |||
<hyphenation-pattern lang="fr" type="xml">file:///doesntexist</hyphenation-pattern> | |||
</fop> |
@@ -0,0 +1,16 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<hyphenation-info> | |||
<hyphen-min before="2" after="3"/> | |||
<exceptions> | |||
</exceptions> | |||
<classes> | |||
</classes> | |||
<patterns> | |||
1he | |||
1lo | |||
</patterns> | |||
</hyphenation-info> |