From cb742f91f7809f8a15157ceb2ed79f0473946c56 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 30 Jul 2013 20:10:35 +0000 Subject: Removed obsolete CommandLineLogger. The Jdk14Logger is guaranteed to be there by default now that the minimal Java requirement is 1.5. Also, CommandLineLogger messes up the output when rendering to stdout. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1508599 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/cli/CommandLineOptions.java | 37 +-- .../apache/fop/fonts/apps/AbstractFontReader.java | 6 - src/java/org/apache/fop/fonts/apps/PFMReader.java | 8 - src/java/org/apache/fop/fonts/apps/TTFReader.java | 8 - .../org/apache/fop/util/CommandLineLogger.java | 263 --------------------- 5 files changed, 2 insertions(+), 320 deletions(-) delete mode 100644 src/java/org/apache/fop/util/CommandLineLogger.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 080fe7930..627b95a47 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -58,7 +58,6 @@ import org.apache.fop.render.pdf.PDFEncryptionOption; import org.apache.fop.render.print.PagesMode; import org.apache.fop.render.print.PrintRenderer; import org.apache.fop.render.xml.XMLRenderer; -import org.apache.fop.util.CommandLineLogger; /** * Options parses the commandline arguments @@ -146,15 +145,6 @@ public class CommandLineOptions { */ public CommandLineOptions() { LogFactory logFactory = LogFactory.getFactory(); - - // Enable the simple command line logging when no other logger is - // defined. - if (System.getProperty("org.apache.commons.logging.Log") == null) { - logFactory.setAttribute("org.apache.commons.logging.Log", - CommandLineLogger.class.getName()); - setLogLevel("info"); - } - log = LogFactory.getLog("FOP"); } @@ -292,7 +282,7 @@ public class CommandLineOptions { } else if (args[i].equals("-s")) { suppressLowLevelAreas = Boolean.TRUE; } else if (args[i].equals("-d")) { - setLogOption("debug", "debug"); + // nop. Left there for backwards compatibility } else if (args[i].equals("-r")) { strictValidation = false; } else if (args[i].equals("-conserve")) { @@ -304,7 +294,7 @@ public class CommandLineOptions { } else if (args[i].equals("-dpi")) { i = i + parseResolution(args, i); } else if (args[i].equals("-q") || args[i].equals("--quiet")) { - setLogOption("quiet", "error"); + // nop. Left there for backwards compatibility } else if (args[i].equals("-fo")) { i = i + parseFOInputOption(args, i); } else if (args[i].equals("-xsl")) { @@ -904,27 +894,6 @@ public class CommandLineOptions { } } - private void setLogOption(String option, String level) { - if (log instanceof CommandLineLogger - || System.getProperty("org.apache.commons.logging.Log") == null) { - setLogLevel(level); - } else if (log != null) { - log.warn("The option " + option + " can only be used"); - log.warn("with FOP's command line logger,"); - log.warn("which is the default on the command line."); - log.warn("Configure other loggers using Java system properties."); - } - } - - private void setLogLevel(String level) { - // Set the level for future loggers. - LogFactory.getFactory().setAttribute("level", level); - if (log instanceof CommandLineLogger) { - // Set the level for the logger created already. - ((CommandLineLogger) log).setLogLevel(level); - } - } - private void setInputFormat(int format) throws FOPException { if (inputmode == NOT_SET || inputmode == format) { inputmode = format; @@ -1208,9 +1177,7 @@ public class CommandLineOptions { + "[-awt|-pdf|-mif|-rtf|-tiff|-png|-pcl|-ps|-txt|-at [mime]|-print] \n" + " [OPTIONS] \n" + " -version print FOP version and exit\n" - + " -d debug mode \n" + " -x dump configuration settings \n" - + " -q quiet mode \n" + " -c cfg.xml use additional configuration file cfg.xml\n" + " -l lang the language to use for user information \n" + " -nocs disable complex script features\n" diff --git a/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java b/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java index 39f7e14a4..3da33ec3f 100644 --- a/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java +++ b/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java @@ -32,8 +32,6 @@ import javax.xml.transform.TransformerFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.util.CommandLineLogger; - /** * Abstract base class for the PFM and TTF Reader command-line applications. */ @@ -90,10 +88,6 @@ public abstract class AbstractFontReader { protected static void setLogLevel(String level) { // Set the evel for future loggers. LogFactory.getFactory().setAttribute("level", level); - if (log instanceof CommandLineLogger) { - // Set the level for the logger creates already. - ((CommandLineLogger) log).setLogLevel(level); - } } /** diff --git a/src/java/org/apache/fop/fonts/apps/PFMReader.java b/src/java/org/apache/fop/fonts/apps/PFMReader.java index e5e8ca524..8ee6ff9b6 100644 --- a/src/java/org/apache/fop/fonts/apps/PFMReader.java +++ b/src/java/org/apache/fop/fonts/apps/PFMReader.java @@ -33,7 +33,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.Version; import org.apache.fop.fonts.type1.PFMFile; -import org.apache.fop.util.CommandLineLogger; /** * A tool which reads PFM files from Adobe Type 1 fonts and creates @@ -92,14 +91,7 @@ public class PFMReader extends AbstractFontReader { Map options = new java.util.HashMap(); String[] arguments = parseArguments(options, args); - // Enable the simple command line logging when no other logger is - // defined. LogFactory logFactory = LogFactory.getFactory(); - if (System.getProperty("org.apache.commons.logging.Log") == null) { - logFactory.setAttribute("org.apache.commons.logging.Log", - CommandLineLogger.class.getName()); - } - determineLogLevel(options); PFMReader app = new PFMReader(); diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java index 900417350..db858e285 100644 --- a/src/java/org/apache/fop/fonts/apps/TTFReader.java +++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java @@ -39,7 +39,6 @@ import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.FontUtil; import org.apache.fop.fonts.truetype.FontFileReader; import org.apache.fop.fonts.truetype.TTFFile; -import org.apache.fop.util.CommandLineLogger; // CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck @@ -123,14 +122,7 @@ public class TTFReader extends AbstractFontReader { Map options = new java.util.HashMap(); String[] arguments = parseArguments(options, args); - // Enable the simple command line logging when no other logger is - // defined. LogFactory logFactory = LogFactory.getFactory(); - if (System.getProperty("org.apache.commons.logging.Log") == null) { - logFactory.setAttribute("org.apache.commons.logging.Log", - CommandLineLogger.class.getName()); - } - determineLogLevel(options); TTFReader app = new TTFReader(); diff --git a/src/java/org/apache/fop/util/CommandLineLogger.java b/src/java/org/apache/fop/util/CommandLineLogger.java deleted file mode 100644 index a9e59158f..000000000 --- a/src/java/org/apache/fop/util/CommandLineLogger.java +++ /dev/null @@ -1,263 +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.util; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * This is a commons-logging logger for command line use. - */ -public class CommandLineLogger implements Log { - /** "Trace" level logging. */ - public static final int LOG_LEVEL_TRACE = 1; - /** "Debug" level logging. */ - public static final int LOG_LEVEL_DEBUG = 2; - /** "Info" level logging. */ - public static final int LOG_LEVEL_INFO = 3; - /** "Warn" level logging. */ - public static final int LOG_LEVEL_WARN = 4; - /** "Error" level logging. */ - public static final int LOG_LEVEL_ERROR = 5; - /** "Fatal" level logging. */ - public static final int LOG_LEVEL_FATAL = 6; - - private int logLevel; - private String logName; - - /** - * Construct the logger with a default log level taken from the LogFactory - * attribute "level". - * @param logName the logger name. - */ - public CommandLineLogger(String logName) { - this.logName = logName; - setLogLevel((String) LogFactory.getFactory().getAttribute("level")); - } - - /** - * Set a log level for the logger. - * @param level the log level - */ - public void setLogLevel(String level) { - if ("fatal".equals(level)) { - logLevel = LOG_LEVEL_FATAL; - } else if ("error".equals(level)) { - logLevel = LOG_LEVEL_ERROR; - } else if ("warn".equals(level)) { - logLevel = LOG_LEVEL_WARN; - } else if ("info".equals(level)) { - logLevel = LOG_LEVEL_INFO; - } else if ("debug".equals(level)) { - logLevel = LOG_LEVEL_DEBUG; - } else if ("trace".equals(level)) { - logLevel = LOG_LEVEL_TRACE; - } else { - logLevel = LOG_LEVEL_INFO; - } - } - - /** - * {@inheritDoc} - */ - public final boolean isTraceEnabled() { - return logLevel <= LOG_LEVEL_TRACE; - } - - /** - * {@inheritDoc} - */ - public final boolean isDebugEnabled() { - return logLevel <= LOG_LEVEL_DEBUG; - } - - /** - * {@inheritDoc} - */ - public final boolean isInfoEnabled() { - return logLevel <= LOG_LEVEL_INFO; - } - - /** - * {@inheritDoc} - */ - public final boolean isWarnEnabled() { - return logLevel <= LOG_LEVEL_WARN; - } - - /** - * {@inheritDoc} - */ - public final boolean isErrorEnabled() { - return logLevel <= LOG_LEVEL_ERROR; - } - - /** - * {@inheritDoc} - */ - public final boolean isFatalEnabled() { - return logLevel <= LOG_LEVEL_FATAL; - } - - /** - * {@inheritDoc} - */ - public final void trace(Object message) { - if (isTraceEnabled()) { - log(LOG_LEVEL_TRACE, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void trace(Object message, Throwable t) { - if (isTraceEnabled()) { - log(LOG_LEVEL_TRACE, message, t); - } - } - - /** - * {@inheritDoc} - */ - public final void debug(Object message) { - if (isDebugEnabled()) { - log(LOG_LEVEL_DEBUG, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void debug(Object message, Throwable t) { - if (isDebugEnabled()) { - log(LOG_LEVEL_DEBUG, message, t); - } - } - - /** - * {@inheritDoc} - */ - public final void info(Object message) { - if (isInfoEnabled()) { - log(LOG_LEVEL_INFO, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void info(Object message, Throwable t) { - if (isInfoEnabled()) { - log(LOG_LEVEL_INFO, message, t); - } - } - - /** - * {@inheritDoc} - */ - public final void warn(Object message) { - if (isWarnEnabled()) { - log(LOG_LEVEL_WARN, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void warn(Object message, Throwable t) { - if (isWarnEnabled()) { - log(LOG_LEVEL_WARN, message, t); - } - } - - /** - * {@inheritDoc} - */ - public final void error(Object message) { - if (isErrorEnabled()) { - log(LOG_LEVEL_ERROR, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void error(Object message, Throwable t) { - if (isErrorEnabled()) { - log(LOG_LEVEL_ERROR, message, t); - } - } - - /** - * {@inheritDoc} - */ - public final void fatal(Object message) { - if (isFatalEnabled()) { - log(LOG_LEVEL_FATAL, message, null); - } - } - - /** - * {@inheritDoc} - */ - public final void fatal(Object message, Throwable t) { - if (isFatalEnabled()) { - log(LOG_LEVEL_FATAL, message, t); - } - } - - /** - * Do the actual logging. - * This method assembles the message and prints it to - * and then calls write() to cause it to be written.

- * - * @param type One of the LOG_LEVEL_XXX constants defining the log level - * @param message The message itself (typically a String) - * @param t The exception whose stack trace should be logged - */ - protected void log(int type, Object message, Throwable t) { - StringBuffer buf = new StringBuffer(); - // Append the message - buf.append(String.valueOf(message)); - if (t != null) { - buf.append("\n"); - // Append a stack trace or just the stack trace message. - if (!isDebugEnabled()) { - buf.append(t.toString()); - buf.append("\n"); - } else { - java.io.StringWriter sw = new java.io.StringWriter(1024); - java.io.PrintWriter pw = new java.io.PrintWriter(sw); - t.printStackTrace(pw); - pw.close(); - buf.append(sw.toString()); - } - } - - // Print to the appropriate destination - if (type >= LOG_LEVEL_WARN) { - System.err.println(buf); - } else { - System.out.println(buf); - } - - } -} -- cgit v1.2.3 From 21682bdca71d422e03b4452fa841b6b542d0c22f Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 14 Aug 2013 22:35:37 +0000 Subject: FOP-2252: OpenType CFF support for FOP; patch submitted by Robert Meyer git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1514076 13f79535-47bb-0310-9956-ffa450edef68 --- findbugs-exclude.xml | 219 ++- lib/fontbox-1.8.0-SNAPSHOT.jar | Bin 0 -> 207498 bytes lib/xmlgraphics-commons-svn-trunk.jar | Bin 629283 -> 629507 bytes .../fonts/OTFAdvancedTypographicTableReader.java | 85 +- src/java/org/apache/fop/fonts/FontLoader.java | 4 +- src/java/org/apache/fop/fonts/MultiByteFont.java | 69 +- src/java/org/apache/fop/fonts/SingleByteFont.java | 2 +- src/java/org/apache/fop/fonts/apps/TTFReader.java | 4 +- .../fop/fonts/autodetect/FontInfoFinder.java | 4 +- .../org/apache/fop/fonts/truetype/GlyfTable.java | 4 +- .../apache/fop/fonts/truetype/OFDirTabEntry.java | 119 ++ .../apache/fop/fonts/truetype/OFFontLoader.java | 261 +++ .../org/apache/fop/fonts/truetype/OFMtxEntry.java | 195 ++ .../org/apache/fop/fonts/truetype/OFTableName.java | 169 ++ .../org/apache/fop/fonts/truetype/OTFFile.java | 109 ++ .../apache/fop/fonts/truetype/OTFSubSetFile.java | 1092 +++++++++++ .../org/apache/fop/fonts/truetype/OpenFont.java | 1937 +++++++++++++++++++ .../apache/fop/fonts/truetype/TTFDirTabEntry.java | 119 -- .../org/apache/fop/fonts/truetype/TTFFile.java | 2011 +------------------- .../apache/fop/fonts/truetype/TTFFontLoader.java | 252 --- .../org/apache/fop/fonts/truetype/TTFMtxEntry.java | 195 -- .../apache/fop/fonts/truetype/TTFSubSetFile.java | 106 +- .../apache/fop/fonts/truetype/TTFTableName.java | 163 -- .../org/apache/fop/pdf/PDFCFFStreamType0C.java | 74 + src/java/org/apache/fop/pdf/PDFEncryptionJCE.java | 99 +- src/java/org/apache/fop/pdf/PDFFactory.java | 57 +- src/java/org/apache/fop/pdf/PDFFontDescriptor.java | 2 + src/java/org/apache/fop/render/ps/PSFontUtils.java | 183 +- src/java/org/apache/fop/render/ps/PSPainter.java | 49 +- .../fop/fonts/cff/CFFDataReaderTestCase.java | 154 ++ .../fop/fonts/truetype/GlyfTableTestCase.java | 7 +- .../apache/fop/fonts/truetype/OTFFileTestCase.java | 86 + .../fop/fonts/truetype/OTFSubSetFileTestCase.java | 148 ++ .../apache/fop/fonts/truetype/TTFFileTestCase.java | 14 +- .../fop/fonts/truetype/TTFFontLoaderTestCase.java | 6 +- .../fop/fonts/truetype/TTFSubSetFileTestCase.java | 7 +- .../fop/fonts/truetype/TTFTableNameTestCase.java | 126 +- test/resources/fonts/otf/AlexBrushRegular.otf | Bin 0 -> 39796 bytes test/resources/fonts/otf/SourceSansProBold.otf | Bin 0 -> 104072 bytes 39 files changed, 5188 insertions(+), 2943 deletions(-) create mode 100644 lib/fontbox-1.8.0-SNAPSHOT.jar create mode 100644 src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OFFontLoader.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OFTableName.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OTFFile.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java create mode 100644 src/java/org/apache/fop/fonts/truetype/OpenFont.java delete mode 100644 src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java delete mode 100644 src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java delete mode 100644 src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java delete mode 100644 src/java/org/apache/fop/fonts/truetype/TTFTableName.java create mode 100644 src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java create mode 100644 test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java create mode 100644 test/java/org/apache/fop/fonts/truetype/OTFFileTestCase.java create mode 100644 test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java create mode 100644 test/resources/fonts/otf/AlexBrushRegular.otf create mode 100644 test/resources/fonts/otf/SourceSansProBold.otf (limited to 'src/java/org/apache/fop') diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 31edec268..2becac909 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -1,7 +1,7 @@ - + @@ -408,6 +408,11 @@ + + + + + @@ -834,7 +839,7 @@ - + @@ -989,7 +994,7 @@ - + @@ -2143,37 +2148,37 @@ - + - + - + - + - + - + - + @@ -2623,7 +2628,7 @@ - + @@ -2852,7 +2857,7 @@ - + @@ -2902,7 +2907,7 @@ - + @@ -3082,7 +3087,7 @@ - + @@ -3747,7 +3752,7 @@ - + @@ -5021,7 +5026,7 @@ - + @@ -5226,4 +5231,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/fontbox-1.8.0-SNAPSHOT.jar b/lib/fontbox-1.8.0-SNAPSHOT.jar new file mode 100644 index 000000000..e20e1ad7b Binary files /dev/null and b/lib/fontbox-1.8.0-SNAPSHOT.jar differ diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar index c7ff1d6a4..22b0f5d52 100644 Binary files a/lib/xmlgraphics-commons-svn-trunk.jar and b/lib/xmlgraphics-commons-svn-trunk.jar differ diff --git a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java index b5457e4e4..4fa6c3b62 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java +++ b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java @@ -29,9 +29,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fonts.truetype.FontFileReader; -import org.apache.fop.fonts.truetype.TTFDirTabEntry; -import org.apache.fop.fonts.truetype.TTFFile; -import org.apache.fop.fonts.truetype.TTFTableName; +import org.apache.fop.fonts.truetype.OFDirTabEntry; +import org.apache.fop.fonts.truetype.OFTableName; +import org.apache.fop.fonts.truetype.OpenFont; // CSOFF: AvoidNestedBlocksCheck // CSOFF: NoWhitespaceAfterCheck @@ -50,7 +50,7 @@ public final class OTFAdvancedTypographicTableReader { // logging state private static Log log = LogFactory.getLog(OTFAdvancedTypographicTableReader.class); // instance state - private TTFFile ttf; // parent font file reader + private OpenFont otf; // parent font file reader private FontFileReader in; // input reader private GlyphDefinitionTable gdef; // glyph definition table private GlyphSubstitutionTable gsub; // glyph substitution table @@ -68,10 +68,10 @@ public final class OTFAdvancedTypographicTableReader { * @param ttf parent font file reader (must be non-null) * @param in font file reader (must be non-null) */ - public OTFAdvancedTypographicTableReader(TTFFile ttf, FontFileReader in) { - assert ttf != null; + public OTFAdvancedTypographicTableReader(OpenFont otf, FontFileReader in) { + assert otf != null; assert in != null; - this.ttf = ttf; + this.otf = otf; this.in = in; } @@ -127,7 +127,8 @@ public final class OTFAdvancedTypographicTableReader { return gpos; } - private void readLangSysTable(TTFTableName tableTag, long langSysTable, String langSysTag) throws IOException { + private void readLangSysTable(OFTableName tableTag, long langSysTable, String langSysTag) + throws IOException { in.seekSet(langSysTable); if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys table: " + langSysTag); @@ -169,7 +170,7 @@ public final class OTFAdvancedTypographicTableReader { private static String defaultTag = "dflt"; - private void readScriptTable(TTFTableName tableTag, long scriptTable, String scriptTag) throws IOException { + private void readScriptTable(OFTableName tableTag, long scriptTable, String scriptTag) throws IOException { in.seekSet(scriptTable); if (log.isDebugEnabled()) { log.debug(tableTag + " script table: " + scriptTag); @@ -222,7 +223,7 @@ public final class OTFAdvancedTypographicTableReader { seLanguages = null; } - private void readScriptList(TTFTableName tableTag, long scriptList) throws IOException { + private void readScriptList(OFTableName tableTag, long scriptList) throws IOException { in.seekSet(scriptList); // read script record count int ns = in.readTTFUShort(); @@ -251,7 +252,7 @@ public final class OTFAdvancedTypographicTableReader { } } - private void readFeatureTable(TTFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException { + private void readFeatureTable(OFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException { in.seekSet(featureTable); if (log.isDebugEnabled()) { log.debug(tableTag + " feature table: " + featureTag); @@ -279,7 +280,7 @@ public final class OTFAdvancedTypographicTableReader { seFeatures.put("f" + featureIndex, new Object[] { featureTag, lul }); } - private void readFeatureList(TTFTableName tableTag, long featureList) throws IOException { + private void readFeatureList(OFTableName tableTag, long featureList) throws IOException { in.seekSet(featureList); // read feature record count int nf = in.readTTFUShort(); @@ -1736,28 +1737,28 @@ public final class OTFAdvancedTypographicTableReader { // XPlacement int xp; if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT) != 0) { - xp = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + xp = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { xp = 0; } // YPlacement int yp; if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT) != 0) { - yp = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + yp = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { yp = 0; } // XAdvance int xa; if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE) != 0) { - xa = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + xa = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { xa = 0; } // YAdvance int ya; if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE) != 0) { - ya = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + ya = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { ya = 0; } @@ -2029,23 +2030,23 @@ public final class OTFAdvancedTypographicTableReader { int af = in.readTTFUShort(); if (af == 1) { // read x coordinate - int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate - int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); a = new GlyphPositioningTable.Anchor(x, y); } else if (af == 2) { // read x coordinate - int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate - int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read anchor point index int ap = in.readTTFUShort(); a = new GlyphPositioningTable.Anchor(x, y, ap); } else if (af == 3) { // read x coordinate - int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate - int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort()); + int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read x device table offset int xdo = in.readTTFUShort(); // read y device table offset @@ -3145,9 +3146,9 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readLookupTable(TTFTableName tableTag, int lookupSequence, long lookupTable) throws IOException { - boolean isGSUB = tableTag.equals(TTFTableName.GSUB); - boolean isGPOS = tableTag.equals(TTFTableName.GPOS); + private void readLookupTable(OFTableName tableTag, int lookupSequence, long lookupTable) throws IOException { + boolean isGSUB = tableTag.equals(OFTableName.GSUB); + boolean isGPOS = tableTag.equals(OFTableName.GPOS); in.seekSet(lookupTable); // read lookup type int lt = in.readTTFUShort(); @@ -3198,7 +3199,7 @@ public final class OTFAdvancedTypographicTableReader { } } - private void readLookupList(TTFTableName tableTag, long lookupList) throws IOException { + private void readLookupList(OFTableName tableTag, long lookupList) throws IOException { in.seekSet(lookupList); // read lookup record count int nl = in.readTTFUShort(); @@ -3233,7 +3234,7 @@ public final class OTFAdvancedTypographicTableReader { * @param lookupList offset to lookup list from beginning of font file * @throws IOException In case of a I/O problem */ - private void readCommonLayoutTables(TTFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException { + private void readCommonLayoutTables(OFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException { if (scriptList > 0) { readScriptList(tableTag, scriptList); } @@ -3245,7 +3246,7 @@ public final class OTFAdvancedTypographicTableReader { } } - private void readGDEFClassDefTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { + private void readGDEFClassDefTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // subtable is a bare class definition table @@ -3257,7 +3258,7 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readGDEFAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { + private void readGDEFAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // read coverage offset @@ -3275,7 +3276,7 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readGDEFLigatureCaretTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { + private void readGDEFLigatureCaretTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // read coverage offset @@ -3305,7 +3306,7 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readGDEFMarkAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { + private void readGDEFMarkAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // subtable is a bare class definition table @@ -3317,7 +3318,7 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readGDEFMarkGlyphsTableFormat1(TTFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException { + private void readGDEFMarkGlyphsTableFormat1(OFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException { initATSubState(); in.seekSet(subtableOffset); // skip over format (already known) @@ -3351,7 +3352,7 @@ public final class OTFAdvancedTypographicTableReader { resetATSubState(); } - private void readGDEFMarkGlyphsTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { + private void readGDEFMarkGlyphsTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read mark set subtable format int sf = in.readTTFUShort(); @@ -3367,17 +3368,17 @@ public final class OTFAdvancedTypographicTableReader { * @throws IOException In case of a I/O problem */ private void readGDEF() throws IOException { - TTFTableName tableTag = TTFTableName.GDEF; + OFTableName tableTag = OFTableName.GDEF; // Initialize temporary state initATState(); // Read glyph definition (GDEF) table - TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag); + OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gdef != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { - ttf.seekTab(in, tableTag, 0); + otf.seekTab(in, tableTag, 0); long version = in.readTTFULong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); @@ -3440,17 +3441,17 @@ public final class OTFAdvancedTypographicTableReader { * @throws IOException In case of a I/O problem */ private void readGSUB() throws IOException { - TTFTableName tableTag = TTFTableName.GSUB; + OFTableName tableTag = OFTableName.GSUB; // Initialize temporary state initATState(); // Read glyph substitution (GSUB) table - TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag); + OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gpos != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { - ttf.seekTab(in, tableTag, 0); + otf.seekTab(in, tableTag, 0); int version = in.readTTFLong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); @@ -3477,17 +3478,17 @@ public final class OTFAdvancedTypographicTableReader { * @throws IOException In case of a I/O problem */ private void readGPOS() throws IOException { - TTFTableName tableTag = TTFTableName.GPOS; + OFTableName tableTag = OFTableName.GPOS; // Initialize temporary state initATState(); // Read glyph positioning (GPOS) table - TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag); + OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gpos != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { - ttf.seekTab(in, tableTag, 0); + otf.seekTab(in, tableTag, 0); int version = in.readTTFLong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java index f7ee24cbc..09e38260e 100644 --- a/src/java/org/apache/fop/fonts/FontLoader.java +++ b/src/java/org/apache/fop/fonts/FontLoader.java @@ -26,7 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.fonts.truetype.TTFFontLoader; +import org.apache.fop.fonts.truetype.OFFontLoader; import org.apache.fop.fonts.type1.Type1FontLoader; /** @@ -105,7 +105,7 @@ public abstract class FontLoader { } loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resourceResolver); } else { - loader = new TTFFontLoader(fontFileURI, subFontName, embedded, embeddingMode, + loader = new OFFontLoader(fontFileURI, subFontName, embedded, embeddingMode, encodingMode, useKerning, useAdvanced, resourceResolver); } return loader.getFont(); diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index 83f6491f3..68be7bed8 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -22,6 +22,7 @@ package org.apache.fop.fonts; import java.nio.CharBuffer; import java.nio.IntBuffer; import java.util.BitSet; +import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.logging.Log; @@ -67,6 +68,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl private int firstUnmapped; private int lastUnmapped; + private boolean isOTFFile = false; + + // since for most users the most likely glyphs are in the first cmap segments we store their mapping. + private static final int NUM_MOST_LIKELY_GLYPHS = 256; + private int[] mostLikelyGlyphs = new int[NUM_MOST_LIKELY_GLYPHS]; + + //A map to store each used glyph from the CID set against the glyph name. + private LinkedHashMap usedGlyphNames = new LinkedHashMap(); + /** * Default constructor */ @@ -111,6 +121,14 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl return cidType; } + public void setIsOTFFile(boolean isOTFFile) { + this.isOTFFile = isOTFFile; + } + + public boolean isOTFFile() { + return this.isOTFFile; + } + /** * Sets the CIDType. * @param cidType The cidType to set @@ -147,6 +165,14 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl return this.cidSet; } + public void mapUsedGlyphName(int gid, String value) { + usedGlyphNames.put(gid, value); + } + + public LinkedHashMap getUsedGlyphNames() { + return usedGlyphNames; + } + /** {@inheritDoc} */ @Override public String getEncodingName() { @@ -177,10 +203,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl * @return the glyph index (or 0 if the glyph is not available) */ // [TBD] - needs optimization, i.e., change from linear search to binary search - private int findGlyphIndex(int c) { + public int findGlyphIndex(int c) { int idx = c; int retIdx = SingleByteEncoding.NOT_FOUND_CODE_POINT; + // for most users the most likely glyphs are in the first cmap segments (meaning the one with + // the lowest unicode start values) + if (idx < NUM_MOST_LIKELY_GLYPHS && mostLikelyGlyphs[idx] != 0) { + return mostLikelyGlyphs[idx]; + } for (int i = 0; (i < cmap.length) && retIdx == 0; i++) { if (cmap[i].getUnicodeStart() <= idx && cmap[i].getUnicodeEnd() >= idx) { @@ -188,6 +219,9 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl retIdx = cmap[i].getGlyphStartIndex() + idx - cmap[i].getUnicodeStart(); + if (idx < NUM_MOST_LIKELY_GLYPHS) { + mostLikelyGlyphs[idx] = retIdx; + } } } return retIdx; @@ -281,22 +315,6 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl return findCharacterFromGlyphIndex(gi, true); } - - /** {@inheritDoc} */ - @Override - public char mapChar(char c) { - notifyMapOperation(); - int glyphIndex = findGlyphIndex(c); - if (glyphIndex == SingleByteEncoding.NOT_FOUND_CODE_POINT) { - warnMissingGlyph(c); - glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); - } - if (isEmbeddable()) { - glyphIndex = cidSet.mapChar(glyphIndex, c); - } - return (char) glyphIndex; - } - protected BitSet getGlyphIndices() { BitSet bitset = new BitSet(); bitset.set(0); @@ -327,6 +345,23 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl return chars; } + /** {@inheritDoc} */ + @Override + public char mapChar(char c) { + notifyMapOperation(); + int glyphIndex = findGlyphIndex(c); + if (glyphIndex == SingleByteEncoding.NOT_FOUND_CODE_POINT) { + warnMissingGlyph(c); + if (!isOTFFile) { + glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); + } + } + if (isEmbeddable()) { + glyphIndex = cidSet.mapChar(glyphIndex, c); + } + return (char) glyphIndex; + } + /** {@inheritDoc} */ @Override public boolean hasChar(char c) { diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java index cd11c7849..a61d846e6 100644 --- a/src/java/org/apache/fop/fonts/SingleByteFont.java +++ b/src/java/org/apache/fop/fonts/SingleByteFont.java @@ -32,7 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.fonts.Glyphs; import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; +import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion; /** * Generic SingleByte font diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java index db858e285..fc003d201 100644 --- a/src/java/org/apache/fop/fonts/apps/TTFReader.java +++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java @@ -38,6 +38,7 @@ import org.apache.fop.Version; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.FontUtil; import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.OFFontLoader; import org.apache.fop.fonts.truetype.TTFFile; // CSOFF: InnerAssignmentCheck @@ -216,7 +217,8 @@ public class TTFReader extends AbstractFontReader { InputStream stream = new FileInputStream(fileName); try { FontFileReader reader = new FontFileReader(stream); - boolean supported = ttfFile.readFont(reader, fontName); + String header = OFFontLoader.readHeader(reader); + boolean supported = ttfFile.readFont(reader, header, fontName); if (!supported) { return null; } diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java index e115264bb..21ebd4937 100644 --- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java +++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java @@ -43,8 +43,8 @@ import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.FontUtil; import org.apache.fop.fonts.MultiByteFont; import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.OFFontLoader; import org.apache.fop.fonts.truetype.TTFFile; -import org.apache.fop.fonts.truetype.TTFFontLoader; /** * Attempts to determine correct FontInfo @@ -220,7 +220,7 @@ public class FontInfoFinder { log.debug("Loading " + fontName); } try { - TTFFontLoader ttfLoader = new TTFFontLoader(fontURI, fontName, true, + OFFontLoader ttfLoader = new OFFontLoader(fontURI, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useAdvanced, resourceResolver); customFont = ttfLoader.getFont(); diff --git a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java index f26ac2ffd..90abccaf2 100644 --- a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java +++ b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java @@ -31,7 +31,7 @@ import java.util.TreeSet; */ public class GlyfTable { - private final TTFMtxEntry[] mtxTab; + private final OFMtxEntry[] mtxTab; private final long tableOffset; @@ -47,7 +47,7 @@ public class GlyfTable { /** All the glyphs that are composed, but do not appear in the subset. */ private Set composedGlyphs = new TreeSet(); - GlyfTable(FontFileReader in, TTFMtxEntry[] metrics, TTFDirTabEntry dirTableEntry, + GlyfTable(FontFileReader in, OFMtxEntry[] metrics, OFDirTabEntry dirTableEntry, Map glyphs) throws IOException { mtxTab = metrics; tableOffset = dirTableEntry.getOffset(); diff --git a/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java new file mode 100644 index 000000000..a9c471d5e --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java @@ -0,0 +1,119 @@ +/* + * 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.truetype; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + + +/** + * This class represents an entry to a TrueType font's Dir Tab. + */ +public class OFDirTabEntry { + + private byte[] tag = new byte[4]; + private int checksum; + private long offset; + private long length; + + public OFDirTabEntry() { + } + + public OFDirTabEntry(long offset, long length) { + this.offset = offset; + this.length = length; + } + + /** + * Read Dir Tab. + * @param in font file reader + * @return tag name + * @throws IOException upon I/O exception + */ + public String read(FontFileReader in) throws IOException { + tag[0] = in.readTTFByte(); + tag[1] = in.readTTFByte(); + tag[2] = in.readTTFByte(); + tag[3] = in.readTTFByte(); + + in.skip(4); // Skip checksum + + offset = in.readTTFULong(); + length = in.readTTFULong(); + String tagStr = new String(tag, "ISO-8859-1"); + + return tagStr; + } + + + @Override + public String toString() { + return "Read dir tab [" + + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]" + + " offset: " + offset + + " length: " + length + + " name: " + tag; + } + + /** + * Returns the checksum. + * @return int + */ + public int getChecksum() { + return checksum; + } + + /** + * Returns the length. + * @return long + */ + public long getLength() { + return length; + } + + /** + * Returns the offset. + * @return long + */ + public long getOffset() { + return offset; + } + + /** + * Returns the tag bytes. + * @return byte[] + */ + public byte[] getTag() { + return tag; + } + + /** + * Returns the tag bytes. + * @return byte[] + */ + public String getTagString() { + try { + return new String(tag, "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + return this.toString(); // Should never happen. + } + } + +} diff --git a/src/java/org/apache/fop/fonts/truetype/OFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/OFFontLoader.java new file mode 100644 index 000000000..f15837bb8 --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OFFontLoader.java @@ -0,0 +1,261 @@ +/* + * 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.truetype; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.io.IOUtils; + +import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.fonts.CIDFontType; +import org.apache.fop.fonts.CMapSegment; +import org.apache.fop.fonts.EmbeddingMode; +import org.apache.fop.fonts.EncodingMode; +import org.apache.fop.fonts.FontLoader; +import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.NamedCharacter; +import org.apache.fop.fonts.SingleByteFont; +import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion; +import org.apache.fop.util.HexEncoder; + +/** + * Loads a TrueType font into memory directly from the original font file. + */ +public class OFFontLoader extends FontLoader { + + private MultiByteFont multiFont; + private SingleByteFont singleFont; + private final String subFontName; + private EncodingMode encodingMode; + private EmbeddingMode embeddingMode; + + /** + * Default constructor + * @param fontFileURI the URI representing the font file + * @param resourceResolver the resource resolver for font URI resolution + */ + public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) { + this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver); + } + + /** + * Additional constructor for TrueType Collections. + * @param fontFileURI the URI representing the font file + * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal + * TrueType fonts) + * @param embedded indicates whether the font is embedded or referenced + * @param embeddingMode the embedding mode of the font + * @param encodingMode the requested encoding mode + * @param useKerning true to enable loading kerning info if available, false to disable + * @param useAdvanced true to enable loading advanced info if available, false to disable + * @param resolver the FontResolver for font URI resolution + */ + public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded, + EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning, + boolean useAdvanced, InternalResourceResolver resolver) { + super(fontFileURI, embedded, useKerning, useAdvanced, resolver); + this.subFontName = subFontName; + this.encodingMode = encodingMode; + this.embeddingMode = embeddingMode; + if (this.encodingMode == EncodingMode.AUTO) { + this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType + } + if (this.embeddingMode == EmbeddingMode.AUTO) { + this.embeddingMode = EmbeddingMode.SUBSET; + } + } + + /** {@inheritDoc} */ + protected void read() throws IOException { + read(this.subFontName); + } + + /** + * Reads a TrueType font. + * @param ttcFontName the TrueType sub-font name of TrueType Collection (may be null for + * normal TrueType fonts) + * @throws IOException if an I/O error occurs + */ + private void read(String ttcFontName) throws IOException { + InputStream in = resourceResolver.getResource(this.fontFileURI); + try { + FontFileReader reader = new FontFileReader(in); + String header = readHeader(reader); + boolean isCFF = header.equals("OTTO"); + OpenFont otf = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced); + boolean supported = otf.readFont(reader, header, ttcFontName); + if (!supported) { + throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI); + } + buildFont(otf, ttcFontName); + loaded = true; + } finally { + IOUtils.closeQuietly(in); + } + } + + public static String readHeader(FontFileReader fontFile) throws IOException { + if (fontFile != null) { + fontFile.seekSet(0); + return fontFile.readTTFString(4); // TTF_FIXED_SIZE (4 bytes) + } + return null; + } + + private void buildFont(OpenFont otf, String ttcFontName) { + boolean isCid = this.embedded; + if (this.encodingMode == EncodingMode.SINGLE_BYTE) { + isCid = false; + } + + if (isCid) { + multiFont = new MultiByteFont(resourceResolver, embeddingMode); + multiFont.setIsOTFFile(otf instanceof OTFFile); + returnFont = multiFont; + multiFont.setTTCName(ttcFontName); + } else { + singleFont = new SingleByteFont(resourceResolver); + returnFont = singleFont; + } + + returnFont.setFontName(otf.getPostScriptName()); + returnFont.setFullName(otf.getFullName()); + returnFont.setFamilyNames(otf.getFamilyNames()); + returnFont.setFontSubFamilyName(otf.getSubFamilyName()); + returnFont.setCapHeight(otf.getCapHeight()); + returnFont.setXHeight(otf.getXHeight()); + returnFont.setAscender(otf.getLowerCaseAscent()); + returnFont.setDescender(otf.getLowerCaseDescent()); + returnFont.setFontBBox(otf.getFontBBox()); + returnFont.setFlags(otf.getFlags()); + returnFont.setStemV(Integer.parseInt(otf.getStemV())); //not used for TTF + returnFont.setItalicAngle(Integer.parseInt(otf.getItalicAngle())); + returnFont.setMissingWidth(0); + returnFont.setWeight(otf.getWeightClass()); + returnFont.setEmbeddingMode(this.embeddingMode); + if (isCid) { + if (otf instanceof OTFFile) { + multiFont.setCIDType(CIDFontType.CIDTYPE0); + } else { + multiFont.setCIDType(CIDFontType.CIDTYPE2); + } + int[] wx = otf.getWidths(); + multiFont.setWidthArray(wx); + } else { + singleFont.setFontType(FontType.TRUETYPE); + singleFont.setEncoding(otf.getCharSetName()); + returnFont.setFirstChar(otf.getFirstChar()); + returnFont.setLastChar(otf.getLastChar()); + singleFont.setTrueTypePostScriptVersion(otf.getPostScriptVersion()); + copyWidthsSingleByte(otf); + } + returnFont.setCMap(getCMap(otf)); + + if (otf.getKerning() != null && useKerning) { + copyKerning(otf, isCid); + } + if (useAdvanced) { + copyAdvanced(otf); + } + if (this.embedded) { + if (otf.isEmbeddable()) { + returnFont.setEmbedURI(this.fontFileURI); + } else { + String msg = "The font " + this.fontFileURI + " is not embeddable due to a" + + " licensing restriction."; + throw new RuntimeException(msg); + } + } + } + + private CMapSegment[] getCMap(OpenFont otf) { + CMapSegment[] array = new CMapSegment[otf.getCMaps().size()]; + return otf.getCMaps().toArray(array); + } + + private void copyWidthsSingleByte(OpenFont otf) { + int[] wx = otf.getWidths(); + for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { + singleFont.setWidth(i, otf.getCharWidth(i)); + } + + for (CMapSegment segment : otf.getCMaps()) { + if (segment.getUnicodeStart() < 0xFFFE) { + for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) { + int codePoint = singleFont.getEncoding().mapChar(u); + if (codePoint <= 0) { + int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart(); + String glyphName = otf.getGlyphName(glyphIndex); + if (glyphName.length() == 0 && otf.getPostScriptVersion() != PostScriptVersion.V2) { + glyphName = "u" + HexEncoder.encode(u); + } + if (glyphName.length() > 0) { + String unicode = Character.toString(u); + NamedCharacter nc = new NamedCharacter(glyphName, unicode); + singleFont.addUnencodedCharacter(nc, wx[glyphIndex]); + } + } + } + } + } + } + + /** + * Copy kerning information. + */ + private void copyKerning(OpenFont otf, boolean isCid) { + + // Get kerning + Set kerningSet; + if (isCid) { + kerningSet = otf.getKerning().keySet(); + } else { + kerningSet = otf.getAnsiKerning().keySet(); + } + + for (Integer kpx1 : kerningSet) { + Map h2; + if (isCid) { + h2 = otf.getKerning().get(kpx1); + } else { + h2 = otf.getAnsiKerning().get(kpx1); + } + returnFont.putKerningEntry(kpx1, h2); + } + } + + /** + * Copy advanced typographic information. + */ + private void copyAdvanced(OpenFont otf) { + if (returnFont instanceof MultiByteFont) { + MultiByteFont mbf = (MultiByteFont) returnFont; + mbf.setGDEF(otf.getGDEF()); + mbf.setGSUB(otf.getGSUB()); + mbf.setGPOS(otf.getGPOS()); + } + } + +} diff --git a/src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java b/src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java new file mode 100644 index 000000000..89af2296f --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java @@ -0,0 +1,195 @@ +/* + * 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.truetype; + +import java.util.List; + +/** + * This class represents a TrueType Mtx Entry. + */ +class OFMtxEntry { + + private int wx; + private int lsb; + private String name = ""; + private int index; + private List unicodeIndex = new java.util.ArrayList(); + private int[] boundingBox = new int[4]; + private long offset; + private byte found = 0; + + /** + * Returns a String representation of this object. + * + * @param t TTFFile to use for unit conversion + * @return String String representation + */ + public String toString(TTFFile t) { + return "Glyph " + name + " index: " + getIndexAsString() + " bbox [" + + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " " + + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " " + + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " " + + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: " + + t.convertTTFUnit2PDFUnit(wx); + } + + /** + * Returns the boundingBox. + * @return int[] + */ + public int[] getBoundingBox() { + return boundingBox; + } + + /** + * Sets the boundingBox. + * @param boundingBox The boundingBox to set + */ + public void setBoundingBox(int[] boundingBox) { + this.boundingBox = boundingBox; + } + + /** + * Returns the found. + * @return byte + */ + public byte getFound() { + return found; + } + + /** + * Returns the index. + * @return int + */ + public int getIndex() { + return index; + } + + /** + * Determines whether this index represents a reserved character. + * @return True if it is reserved + */ + public boolean isIndexReserved() { + return (getIndex() >= 32768) && (getIndex() <= 65535); + } + + /** + * Returns a String representation of the index taking into account if + * the index is in the reserved range. + * @return index as String + */ + public String getIndexAsString() { + if (isIndexReserved()) { + return Integer.toString(getIndex()) + " (reserved)"; + } else { + return Integer.toString(getIndex()); + } + } + + /** + * Returns the lsb. + * @return int + */ + public int getLsb() { + return lsb; + } + + /** + * Returns the name. + * @return String + */ + public String getName() { + return name; + } + + /** + * Returns the offset. + * @return long + */ + public long getOffset() { + return offset; + } + + /** + * Returns the unicodeIndex. + * @return List + */ + public List getUnicodeIndex() { + return unicodeIndex; + } + + /** + * Returns the wx. + * @return int + */ + public int getWx() { + return wx; + } + + /** + * Sets the found. + * @param found The found to set + */ + public void setFound(byte found) { + this.found = found; + } + + /** + * Sets the index. + * @param index The index to set + */ + public void setIndex(int index) { + this.index = index; + } + + /** + * Sets the lsb. + * @param lsb The lsb to set + */ + public void setLsb(int lsb) { + this.lsb = lsb; + } + + /** + * Sets the name. + * @param name The name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the offset. + * @param offset The offset to set + */ + public void setOffset(long offset) { + this.offset = offset; + } + + /** + * Sets the wx. + * @param wx The wx to set + */ + public void setWx(int wx) { + this.wx = wx; + } + + +} diff --git a/src/java/org/apache/fop/fonts/truetype/OFTableName.java b/src/java/org/apache/fop/fonts/truetype/OFTableName.java new file mode 100644 index 000000000..f6264129a --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OFTableName.java @@ -0,0 +1,169 @@ +/* + * 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.truetype; + + +/** + * Represents table names as found in a TrueType font's Table Directory. + * TrueType fonts may have custom tables so we cannot use an enum. + */ +public final class OFTableName { + + /** The first table in a TrueType font file containing metadata about other tables. */ + public static final OFTableName TABLE_DIRECTORY = new OFTableName("tableDirectory"); + + /** Baseline data */ + public static final OFTableName BASE = new OFTableName("BASE"); + + /** CFF data/ */ + public static final OFTableName CFF = new OFTableName("CFF "); + + /** Embedded bitmap data. */ + public static final OFTableName EBDT = new OFTableName("EBDT"); + + /** Embedded bitmap location data. */ + public static final OFTableName EBLC = new OFTableName("EBLC"); + + /** Embedded bitmap scaling data. */ + public static final OFTableName EBSC = new OFTableName("EBSC"); + + /** A FontForge specific table. */ + public static final OFTableName FFTM = new OFTableName("FFTM"); + + /** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */ + public static final OFTableName GDEF = new OFTableName("GDEF"); + + /** Provides kerning information, mark-to-base, etc. for opentype fonts. */ + public static final OFTableName GPOS = new OFTableName("GPOS"); + + /** Provides ligature information, swash, etc. for opentype fonts. */ + public static final OFTableName GSUB = new OFTableName("GSUB"); + + /** Linear threshold table. */ + public static final OFTableName LTSH = new OFTableName("LTSH"); + + /** OS/2 and Windows specific metrics. */ + public static final OFTableName OS2 = new OFTableName("OS/2"); + + /** PCL 5 data. */ + public static final OFTableName PCLT = new OFTableName("PCLT"); + + /** Vertical Device Metrics table. */ + public static final OFTableName VDMX = new OFTableName("VDMX"); + + /** Character to glyph mapping. */ + public static final OFTableName CMAP = new OFTableName("cmap"); + + /** Control Value Table. */ + public static final OFTableName CVT = new OFTableName("cvt "); + + /** Font program. */ + public static final OFTableName FPGM = new OFTableName("fpgm"); + + /** Grid-fitting and scan conversion procedure (grayscale). */ + public static final OFTableName GASP = new OFTableName("gasp"); + + /** Glyph data. */ + public static final OFTableName GLYF = new OFTableName("glyf"); + + /** Horizontal device metrics. */ + public static final OFTableName HDMX = new OFTableName("hdmx"); + + /** Font header. */ + public static final OFTableName HEAD = new OFTableName("head"); + + /** Horizontal header. */ + public static final OFTableName HHEA = new OFTableName("hhea"); + + /** Horizontal metrics. */ + public static final OFTableName HMTX = new OFTableName("hmtx"); + + /** Kerning. */ + public static final OFTableName KERN = new OFTableName("kern"); + + /** Index to location. */ + public static final OFTableName LOCA = new OFTableName("loca"); + + /** Maximum profile. */ + public static final OFTableName MAXP = new OFTableName("maxp"); + + /** Naming table. */ + public static final OFTableName NAME = new OFTableName("name"); + + /** PostScript information. */ + public static final OFTableName POST = new OFTableName("post"); + + /** CVT Program. */ + public static final OFTableName PREP = new OFTableName("prep"); + + /** Vertical Metrics header. */ + public static final OFTableName VHEA = new OFTableName("vhea"); + + /** Vertical Metrics. */ + public static final OFTableName VMTX = new OFTableName("vmtx"); + + private final String name; + + private OFTableName(String name) { + this.name = name; + } + + /** + * Returns the name of the table as it should be in the Directory Table. + */ + public String getName() { + return name; + } + + /** + * Returns an instance of this class corresponding to the given string representation. + * @param tableName table name as in the Table Directory + * @return TTFTableName + */ + public static OFTableName getValue(String tableName) { + if (tableName != null) { + return new OFTableName(tableName); + } + throw new IllegalArgumentException("A TrueType font table name must not be null"); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof OFTableName)) { + return false; + } + OFTableName to = (OFTableName) o; + return this.name.equals(to.getName()); + } + + @Override + public String toString() { + return name; + } + +} diff --git a/src/java/org/apache/fop/fonts/truetype/OTFFile.java b/src/java/org/apache/fop/fonts/truetype/OTFFile.java new file mode 100644 index 000000000..3976b5994 --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OTFFile.java @@ -0,0 +1,109 @@ +/* + * 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.truetype; + +import java.io.IOException; + +import org.apache.fontbox.cff.CFFDataInput; +import org.apache.fontbox.cff.CFFFont; +import org.apache.fontbox.cff.CFFFont.Mapping; +import org.apache.fontbox.cff.CFFParser; + +public class OTFFile extends OpenFont { + + protected CFFFont fileFont; + + public OTFFile() throws IOException { + checkForFontbox(); + } + + private void checkForFontbox() throws IOException { + try { + Class.forName("org.apache.fontbox.cff.CFFFont"); + } catch (ClassNotFoundException ex) { + throw new IOException("The Fontbox jar was not found in the classpath. This is " + + "required for OTF CFF ssupport."); + } + } + + @Override + protected void updateBBoxAndOffset() throws IOException { + UnicodeMapping[] mappings = unicodeMappings.toArray(new UnicodeMapping[0]); + for (int i = 0; i < mappings.length; i++) { + int glyphIdx = mappings[i].getGlyphIndex(); + Mapping m = fileFont.getGIDMappings().get(glyphIdx); + int[] bbox = fileFont.getBoundingBox(m.getSID()); + String name = fileFont.getNameOfCharFromCode(m.getSID()); + mtxTab[glyphIdx].setBoundingBox(bbox); + mtxTab[glyphIdx].setName(name); + } + } + + @Override + protected void initializeFont(FontFileReader in) throws IOException { + fontFile = in; + fontFile.seekSet(0); + CFFParser parser = new CFFParser(); + fileFont = parser.parse(in.getAllBytes()).get(0); + } + + protected void readName() throws IOException { + Object familyName = fileFont.getProperty("FamilyName"); + if (familyName != null && !familyName.equals("")) { + familyNames.add(familyName.toString()); + fullName = familyName.toString(); + } else { + fullName = fileFont.getName(); + familyNames.add(fullName); + } + } + + /** + * Reads the CFFData from a given font file + * @param fontFile The font file being read + * @return The byte data found in the CFF table + */ + public static byte[] getCFFData(FontFileReader fontFile) throws IOException { + byte[] cff = new byte[0]; + CFFDataInput input = new CFFDataInput(fontFile.getAllBytes()); + input.readBytes(4); //OTTO + short numTables = input.readShort(); + input.readShort(); //searchRange + input.readShort(); //entrySelector + input.readShort(); //rangeShift + + for (int q = 0; q < numTables; q++) { + String tagName = new String(input.readBytes(4)); + readLong(input); //Checksum + long offset = readLong(input); + long length = readLong(input); + if (tagName.equals("CFF ")) { + cff = new byte[(int)length]; + System.arraycopy(fontFile.getAllBytes(), (int)offset, cff, 0, cff.length); + break; + } + } + return cff; + } + + private static long readLong(CFFDataInput input) throws IOException { + return (input.readCard16() << 16) | input.readCard16(); + } +} diff --git a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java new file mode 100644 index 000000000..dbea48216 --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -0,0 +1,1092 @@ +/* + * 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.truetype; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.fontbox.cff.CFFStandardString; +import org.apache.fontbox.cff.encoding.CFFEncoding; + +import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.cff.CFFDataReader; +import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData; +import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry; +import org.apache.fop.fonts.cff.CFFDataReader.FDSelect; +import org.apache.fop.fonts.cff.CFFDataReader.FontDict; +import org.apache.fop.fonts.cff.CFFDataReader.Format0FDSelect; +import org.apache.fop.fonts.cff.CFFDataReader.Format3FDSelect; + +/** + * Reads an OpenType CFF file and generates a subset + * The OpenType specification can be found at the Microsoft + * Typography site: http://www.microsoft.com/typography/otspec/ + */ +public class OTFSubSetFile extends OTFFile { + + private byte[] output; + private int currentPos = 0; + private int realSize = 0; + + /** A map containing each glyph to be included in the subset + * with their existing and new GID's **/ + private LinkedHashMap subsetGlyphs; + + /** A map of the new GID to SID used to construct the charset table **/ + private LinkedHashMap gidToSID; + + private CFFIndexData localIndexSubr; + private CFFIndexData globalIndexSubr; + + /** List of subroutines to write to the local / global indexes in the subset font **/ + private List subsetLocalIndexSubr; + private List subsetGlobalIndexSubr; + + /** For fonts which have an FDSelect or ROS flag in Top Dict, this is used to store the + * local subroutine indexes for each group as opposed to the above subsetLocalIndexSubr */ + private ArrayList> fdSubrs; + + /** The subset FD Select table used to store the mappings between glyphs and their + * associated FDFont object which point to a private dict and local subroutines. */ + private LinkedHashMap subsetFDSelect; + + /** A list of unique subroutines from the global / local subroutine indexes */ + private List localUniques; + private List globalUniques; + + /** A store of the number of subroutines each global / local subroutine will store **/ + private int subsetLocalSubrCount; + private int subsetGlobalSubrCount; + + /** A list of char string data for each glyph to be stored in the subset font **/ + private List subsetCharStringsIndex; + + /** The embedded name to change in the name table **/ + private String embeddedName; + + /** An array used to hold the string index data for the subset font **/ + private List stringIndexData = new ArrayList(); + + /** The CFF reader object used to read data and offsets from the original font file */ + private CFFDataReader cffReader = null; + + /** The class used to represent this font **/ + private MultiByteFont mbFont; + + /** The number of standard strings in CFF **/ + private static final int NUM_STANDARD_STRINGS = 391; + /** The operator used to identify a local subroutine reference */ + private static final int LOCAL_SUBROUTINE = 10; + /** The operator used to identify a global subroutine reference */ + private static final int GLOBAL_SUBROUTINE = 29; + + public OTFSubSetFile() throws IOException { + super(); + } + + public void readFont(FontFileReader in, String embeddedName, String header, + MultiByteFont mbFont) throws IOException { + this.mbFont = mbFont; + readFont(in, embeddedName, header, mbFont.getUsedGlyphs()); + } + + /** + * Reads and creates a subset of the font. + * + * @param in FontFileReader to read from + * @param name Name to be checked for in the font file + * @param header The header of the font file + * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and + * new index as (Integer) value) + * @throws IOException in case of an I/O problem + */ + void readFont(FontFileReader in, String embeddedName, String header, + Map usedGlyphs) throws IOException { + fontFile = in; + + currentPos = 0; + realSize = 0; + + this.embeddedName = embeddedName; + + //Sort by the new GID and store in a LinkedHashMap + subsetGlyphs = sortByValue(usedGlyphs); + + output = new byte[in.getFileSize()]; + + initializeFont(in); + + cffReader = new CFFDataReader(fontFile); + + //Create the CIDFontType0C data + createCFF(); + } + + private LinkedHashMap sortByValue(Map map) { + List> list = new ArrayList>(map.entrySet()); + Collections.sort(list, new Comparator>() { + public int compare(Entry o1, Entry o2) { + return ((Comparable) o1.getValue()).compareTo(o2.getValue()); + } + }); + + LinkedHashMap result = new LinkedHashMap(); + for (Entry entry : list) { + result.put(entry.getKey(), entry.getValue()); + } + return result; + } + + private void createCFF() throws IOException { + //Header + writeBytes(cffReader.getHeader()); + + //Name Index + writeIndex(Arrays.asList(embeddedName.getBytes())); + + //Keep offset of the topDICT so it can be updated once all data has been written + int topDictOffset = currentPos; + //Top DICT Index and Data + byte[] topDictIndex = cffReader.getTopDictIndex().getByteData(); + int offSize = topDictIndex[2]; + writeBytes(topDictIndex, 0, 3 + (offSize * 2)); + int topDictDataOffset = currentPos; + writeTopDICT(); + + //Create the char string index data and related local / global subroutines + if (cffReader.getFDSelect() == null) { + createCharStringData(); + } else { + createCharStringDataCID(); + } + + //If it is a CID-Keyed font, store each FD font and add each SID + List fontNameSIDs = null; + List subsetFDFonts = null; + if (cffReader.getFDSelect() != null) { + subsetFDFonts = getUsedFDFonts(); + fontNameSIDs = storeFDStrings(subsetFDFonts); + } + + //String index + writeStringIndex(); + + //Global subroutine index + writeIndex(subsetGlobalIndexSubr); + + //Encoding + int encodingOffset = currentPos; + writeEncoding(fileFont.getEncoding()); + + //Charset table + int charsetOffset = currentPos; + writeCharsetTable(cffReader.getFDSelect() != null); + + //FDSelect table + int fdSelectOffset = currentPos; + if (cffReader.getFDSelect() != null) { + writeFDSelect(); + } + + //Char Strings Index + int charStringOffset = currentPos; + writeIndex(subsetCharStringsIndex); + + if (cffReader.getFDSelect() == null) { + //Keep offset to modify later with the local subroutine index offset + int privateDictOffset = currentPos; + writePrivateDict(); + + //Local subroutine index + int localIndexOffset = currentPos; + writeIndex(subsetLocalIndexSubr); + + //Update the offsets + updateOffsets(topDictOffset, charsetOffset, charStringOffset, privateDictOffset, + localIndexOffset, encodingOffset); + } else { + List privateDictOffsets = writeCIDDictsAndSubrs(subsetFDFonts); + int fdArrayOffset = writeFDArray(subsetFDFonts, privateDictOffsets, fontNameSIDs); + + updateCIDOffsets(topDictDataOffset, fdArrayOffset, fdSelectOffset, charsetOffset, + charStringOffset, encodingOffset); + } + } + + private List storeFDStrings(List uniqueNewRefs) throws IOException { + ArrayList fontNameSIDs = new ArrayList(); + List fdFonts = cffReader.getFDFonts(); + for (int i = 0; i < uniqueNewRefs.size(); i++) { + FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i)); + byte[] fdFontByteData = fdFont.getByteData(); + Map fdFontDict = cffReader.parseDictData(fdFontByteData); + fontNameSIDs.add(stringIndexData.size() + NUM_STANDARD_STRINGS); + stringIndexData.add(cffReader.getStringIndex().getValue(fdFontDict.get("FontName") + .getOperands().get(0).intValue() - NUM_STANDARD_STRINGS)); + } + return fontNameSIDs; + } + + private void writeBytes(byte[] out) { + for (int i = 0; i < out.length; i++) { + output[currentPos++] = out[i]; + realSize++; + } + } + + private void writeBytes(byte[] out, int offset, int length) { + for (int i = offset; i < offset + length; i++) { + output[currentPos++] = out[i]; + realSize++; + } + } + + private void writeEncoding(CFFEncoding encoding) throws IOException { + LinkedHashMap topDICT = cffReader.getTopDictEntries(); + DICTEntry encodingEntry = topDICT.get("Encoding"); + if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0 + && encodingEntry.getOperands().get(0).intValue() != 1) { + writeByte(0); + writeByte(gidToSID.size()); + for (int gid : gidToSID.keySet()) { + int code = encoding.getCode(gidToSID.get(gid)); + writeByte(code); + } + } + } + + private void writeTopDICT() throws IOException { + LinkedHashMap topDICT = cffReader.getTopDictEntries(); + List topDictStringEntries = Arrays.asList("version", "Notice", "Copyright", + "FullName", "FamilyName", "Weight", "PostScript"); + for (Map.Entry dictEntry : topDICT.entrySet()) { + String dictKey = dictEntry.getKey(); + DICTEntry entry = dictEntry.getValue(); + //If the value is an SID, update the reference but keep the size the same + if (dictKey.equals("ROS")) { + writeROSEntry(entry); + } else if (dictKey.equals("CIDCount")) { + writeCIDCount(entry); + } else if (topDictStringEntries.contains(dictKey)) { + writeTopDictStringEntry(entry); + } else { + writeBytes(entry.getByteData()); + } + } + } + + private void writeROSEntry(DICTEntry dictEntry) throws IOException { + int sidA = dictEntry.getOperands().get(0).intValue(); + if (sidA > 390) { + stringIndexData.add(cffReader.getStringIndex().getValue(sidA - NUM_STANDARD_STRINGS)); + } + int sidAStringIndex = stringIndexData.size() + 390; + int sidB = dictEntry.getOperands().get(1).intValue(); + if (sidB > 390) { + stringIndexData.add("Identity".getBytes()); + } + int sidBStringIndex = stringIndexData.size() + 390; + byte[] cidEntryByteData = dictEntry.getByteData(); + cidEntryByteData = updateOffset(cidEntryByteData, 0, dictEntry.getOperandLengths().get(0), + sidAStringIndex); + cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0), + dictEntry.getOperandLengths().get(1), sidBStringIndex); + cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0) + + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 139); + writeBytes(cidEntryByteData); + } + + private void writeCIDCount(DICTEntry dictEntry) throws IOException { + byte[] cidCountByteData = dictEntry.getByteData(); + cidCountByteData = updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0), + subsetGlyphs.size()); + writeBytes(cidCountByteData); + } + + private void writeTopDictStringEntry(DICTEntry dictEntry) throws IOException { + int sid = dictEntry.getOperands().get(0).intValue(); + if (sid > 391) { + stringIndexData.add(cffReader.getStringIndex().getValue(sid - 391)); + } + + byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(), + dictEntry.getOperandLength()); + writeBytes(newDictEntry); + } + + private void writeStringIndex() throws IOException { + Map topDICT = cffReader.getTopDictEntries(); + int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue(); + + gidToSID = new LinkedHashMap(); + + for (int gid : subsetGlyphs.keySet()) { + int sid = cffReader.getSIDFromGID(charsetOffset, gid); + //Check whether the SID falls into the standard string set + if (sid < NUM_STANDARD_STRINGS) { + gidToSID.put(subsetGlyphs.get(gid), sid); + if (mbFont != null) { + mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), + CFFStandardString.getName(sid)); + } + } else { + int index = sid - NUM_STANDARD_STRINGS; + if (index <= cffReader.getStringIndex().getNumObjects()) { + if (mbFont != null) { + mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), + new String(cffReader.getStringIndex().getValue(index))); + } + gidToSID.put(subsetGlyphs.get(gid), stringIndexData.size() + 391); + stringIndexData.add(cffReader.getStringIndex().getValue(index)); + } else { + if (mbFont != null) { + mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), ".notdef"); + } + gidToSID.put(subsetGlyphs.get(gid), index); + } + } + } + //Write the String Index + writeIndex(stringIndexData); + } + + private void createCharStringDataCID() throws IOException { + CFFIndexData charStringsIndex = cffReader.getCharStringIndex(); + + FDSelect fontDictionary = cffReader.getFDSelect(); + if (fontDictionary instanceof Format0FDSelect) { + throw new UnsupportedOperationException("OTF CFF CID Format0 currently not implemented"); + } else if (fontDictionary instanceof Format3FDSelect) { + Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary; + Map subsetGroups = new HashMap(); + + List uniqueGroups = new ArrayList(); + for (int gid : subsetGlyphs.keySet()) { + Integer[] ranges = fdSelect.getRanges().keySet().toArray(new Integer[0]); + for (int i = 0; i < ranges.length; i++) { + int nextRange = -1; + if (i < ranges.length - 1) { + nextRange = ranges[i + 1]; + } else { + nextRange = fdSelect.getSentinelGID(); + } + if (gid >= ranges[i] && gid < nextRange) { + subsetGroups.put(gid, fdSelect.getRanges().get(ranges[i])); + if (!uniqueGroups.contains(fdSelect.getRanges().get(ranges[i]))) { + uniqueGroups.add(fdSelect.getRanges().get(ranges[i])); + } + } + } + } + + //Prepare resources + globalIndexSubr = cffReader.getGlobalIndexSubr(); + + //Create the new char string index + subsetCharStringsIndex = new ArrayList(); + + globalUniques = new ArrayList(); + + subsetFDSelect = new LinkedHashMap(); + + List> foundLocalUniques = new ArrayList>(); + for (int i = 0; i < uniqueGroups.size(); i++) { + foundLocalUniques.add(new ArrayList()); + } + for (int gid : subsetGlyphs.keySet()) { + int group = subsetGroups.get(gid); + localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData(); + localUniques = foundLocalUniques.get(uniqueGroups.indexOf(subsetGroups.get(gid))); + + FDIndexReference newFDReference = new FDIndexReference( + uniqueGroups.indexOf(subsetGroups.get(gid)), subsetGroups.get(gid)); + subsetFDSelect.put(subsetGlyphs.get(gid), newFDReference); + byte[] data = charStringsIndex.getValue(gid); + preScanForSubsetIndexSize(data); + } + + //Create the two lists which are to store the local and global subroutines + subsetGlobalIndexSubr = new ArrayList(); + + fdSubrs = new ArrayList>(); + subsetGlobalSubrCount = globalUniques.size(); + globalUniques.clear(); + localUniques = null; + + for (int l = 0; l < foundLocalUniques.size(); l++) { + fdSubrs.add(new ArrayList()); + } + List> foundLocalUniquesB = new ArrayList>(); + for (int k = 0; k < uniqueGroups.size(); k++) { + foundLocalUniquesB.add(new ArrayList()); + } + for (Integer gid : subsetGlyphs.keySet()) { + int group = subsetGroups.get(gid); + localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData(); + localUniques = foundLocalUniquesB.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()); + byte[] data = charStringsIndex.getValue(gid); + subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()); + subsetLocalSubrCount = foundLocalUniques.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()).size(); + data = readCharStringData(data, subsetLocalSubrCount); + subsetCharStringsIndex.add(data); + } + } + } + + private void writeFDSelect() { + writeByte(0); //Format + for (Integer gid : subsetFDSelect.keySet()) { + writeByte(subsetFDSelect.get(gid).getNewFDIndex()); + } + } + + private List getUsedFDFonts() { + List uniqueNewRefs = new ArrayList(); + for (int gid : subsetFDSelect.keySet()) { + int fdIndex = subsetFDSelect.get(gid).getOldFDIndex(); + if (!uniqueNewRefs.contains(fdIndex)) { + uniqueNewRefs.add(fdIndex); + } + } + return uniqueNewRefs; + } + + private List writeCIDDictsAndSubrs(List uniqueNewRefs) + throws IOException { + List privateDictOffsets = new ArrayList(); + List fdFonts = cffReader.getFDFonts(); + for (int i = 0; i < uniqueNewRefs.size(); i++) { + FontDict curFDFont = fdFonts.get(uniqueNewRefs.get(i)); + HashMap fdPrivateDict = cffReader.parseDictData( + curFDFont.getPrivateDictData()); + int privateDictOffset = currentPos; + privateDictOffsets.add(privateDictOffset); + byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData(); + if (fdPrivateDict.get("Subrs") != null) { + fdPrivateDictByteData = updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(), + fdPrivateDict.get("Subrs").getOperandLength(), + fdPrivateDictByteData.length); + } + writeBytes(fdPrivateDictByteData); + writeIndex(fdSubrs.get(i)); + } + return privateDictOffsets; + } + + private int writeFDArray(List uniqueNewRefs, List privateDictOffsets, + List fontNameSIDs) + throws IOException { + int offset = currentPos; + List fdFonts = cffReader.getFDFonts(); + + writeCard16(uniqueNewRefs.size()); + writeByte(1); //Offset size + writeByte(1); //First offset + + int count = 1; + for (int i = 0; i < uniqueNewRefs.size(); i++) { + FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i)); + count += fdFont.getByteData().length; + writeByte(count); + } + + for (int i = 0; i < uniqueNewRefs.size(); i++) { + FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i)); + byte[] fdFontByteData = fdFont.getByteData(); + Map fdFontDict = cffReader.parseDictData(fdFontByteData); + //Update the SID to the FontName + fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("FontName").getOffset() - 1, + fdFontDict.get("FontName").getOperandLengths().get(0), + fontNameSIDs.get(i)); + //Update the Private dict reference + fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("Private").getOffset() + + fdFontDict.get("Private").getOperandLengths().get(0), + fdFontDict.get("Private").getOperandLengths().get(1), + privateDictOffsets.get(i)); + writeBytes(fdFontByteData); + } + return offset; + } + + private class FDIndexReference { + private int newFDIndex; + private int oldFDIndex; + + public FDIndexReference(int newFDIndex, int oldFDIndex) { + this.newFDIndex = newFDIndex; + this.oldFDIndex = oldFDIndex; + } + + public int getNewFDIndex() { + return newFDIndex; + } + + public int getOldFDIndex() { + return oldFDIndex; + } + } + + private void createCharStringData() throws IOException { + Map topDICT = cffReader.getTopDictEntries(); + + CFFIndexData charStringsIndex = cffReader.getCharStringIndex(); + + DICTEntry privateEntry = topDICT.get("Private"); + if (privateEntry != null) { + int privateOffset = privateEntry.getOperands().get(1).intValue(); + Map privateDICT = cffReader.getPrivateDict(privateEntry); + + int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue(); + localIndexSubr = cffReader.readIndex(localSubrOffset); + } + + globalIndexSubr = cffReader.getGlobalIndexSubr(); + + //Create the two lists which are to store the local and global subroutines + subsetLocalIndexSubr = new ArrayList(); + subsetGlobalIndexSubr = new ArrayList(); + + //Create the new char string index + subsetCharStringsIndex = new ArrayList(); + + localUniques = new ArrayList(); + globalUniques = new ArrayList(); + + for (int gid : subsetGlyphs.keySet()) { + byte[] data = charStringsIndex.getValue(gid); + preScanForSubsetIndexSize(data); + } + + //Store the size of each subset index and clear the unique arrays + subsetLocalSubrCount = localUniques.size(); + subsetGlobalSubrCount = globalUniques.size(); + localUniques.clear(); + globalUniques.clear(); + + for (int gid : subsetGlyphs.keySet()) { + byte[] data = charStringsIndex.getValue(gid); + //Retrieve modified char string data and fill local / global subroutine arrays + data = readCharStringData(data, subsetLocalSubrCount); + subsetCharStringsIndex.add(data); + } + } + + private void preScanForSubsetIndexSize(byte[] data) throws IOException { + boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; + boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; + BytesNumber operand = new BytesNumber(-1, -1); + for (int dataPos = 0; dataPos < data.length; dataPos++) { + int b0 = data[dataPos] & 0xff; + if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) { + int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber()); + + if (!localUniques.contains(subrNumber) && subrNumber < localIndexSubr.getNumObjects()) { + localUniques.add(subrNumber); + byte[] subr = localIndexSubr.getValue(subrNumber); + preScanForSubsetIndexSize(subr); + } + operand.clearNumber(); + } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) { + int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber()); + + if (!globalUniques.contains(subrNumber) && subrNumber < globalIndexSubr.getNumObjects()) { + globalUniques.add(subrNumber); + byte[] subr = globalIndexSubr.getValue(subrNumber); + preScanForSubsetIndexSize(subr); + } + operand.clearNumber(); + } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { + operand.clearNumber(); + if (b0 == 19 || b0 == 20) { + dataPos += 1; + } + } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { + operand = readNumber(b0, data, dataPos); + dataPos += operand.getNumBytes() - 1; + } + } + } + + private int getSubrNumber(int numSubroutines, int operand) { + int bias = getBias(numSubroutines); + return bias + operand; + } + + private byte[] readCharStringData(byte[] data, int subsetLocalSubrCount) throws IOException { + boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; + boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; + BytesNumber operand = new BytesNumber(-1, -1); + for (int dataPos = 0; dataPos < data.length; dataPos++) { + int b0 = data[dataPos] & 0xff; + if (b0 == 10 && hasLocalSubroutines) { + int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber()); + + int newRef = getNewRefForReference(subrNumber, localUniques, localIndexSubr, subsetLocalIndexSubr, + subsetLocalSubrCount); + + if (newRef != -1) { + byte[] newData = constructNewRefData(dataPos, data, operand, subsetLocalSubrCount, + newRef, new int[] {10}); + dataPos -= data.length - newData.length; + data = newData; + } + + operand.clearNumber(); + } else if (b0 == 29 && hasGlobalSubroutines) { + int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber()); + + int newRef = getNewRefForReference(subrNumber, globalUniques, globalIndexSubr, subsetGlobalIndexSubr, + subsetGlobalSubrCount); + + if (newRef != -1) { + byte[] newData = constructNewRefData(dataPos, data, operand, subsetGlobalSubrCount, + newRef, new int[] {29}); + dataPos -= (data.length - newData.length); + data = newData; + } + + operand.clearNumber(); + } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { + operand.clearNumber(); + if (b0 == 19 || b0 == 20) { + dataPos += 1; + } + } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { + operand = readNumber(b0, data, dataPos); + dataPos += operand.getNumBytes() - 1; + } + } + + //Return the data with the modified references to our arrays + return data; + } + + private int getNewRefForReference(int subrNumber, List uniquesArray, + CFFIndexData indexSubr, List subsetIndexSubr, int subrCount) throws IOException { + int newRef = -1; + if (!uniquesArray.contains(subrNumber)) { + if (subrNumber < indexSubr.getNumObjects()) { + byte[] subr = indexSubr.getValue(subrNumber); + subr = readCharStringData(subr, subrCount); + if (!uniquesArray.contains(subrNumber)) { + uniquesArray.add(subrNumber); + subsetIndexSubr.add(subr); + newRef = subsetIndexSubr.size() - 1; + } else { + newRef = uniquesArray.indexOf(subrNumber); + } + } + } else { + newRef = uniquesArray.indexOf(subrNumber); + } + return newRef; + } + + private int getBias(int subrCount) { + if (subrCount < 1240) { + return 107; + } else if (subrCount < 33900) { + return 1131; + } else { + return 32768; + } + } + + private byte[] constructNewRefData(int curDataPos, byte[] currentData, BytesNumber operand, + int fullSubsetIndexSize, int curSubsetIndexSize, int[] operatorCode) { + //Create the new array with the modified reference + byte[] newData; + int startRef = curDataPos - operand.getNumBytes(); + int length = operand.getNumBytes() + 1; + byte[] preBytes = new byte[startRef]; + System.arraycopy(currentData, 0, preBytes, 0, startRef); + int newBias = getBias(fullSubsetIndexSize); + int newRef = curSubsetIndexSize - newBias; + byte[] newRefBytes = createNewRef(newRef, operatorCode, -1); + newData = concatArray(preBytes, newRefBytes); + byte[] postBytes = new byte[currentData.length - (startRef + length)]; + System.arraycopy(currentData, startRef + length, postBytes, 0, + currentData.length - (startRef + length)); + return concatArray(newData, postBytes); + } + + public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength) { + byte[] newRefBytes; + int sizeOfOperator = operatorCode.length; + if ((forceLength == -1 && newRef <= 107) || forceLength == 1) { + newRefBytes = new byte[1 + sizeOfOperator]; + //The index values are 0 indexed + newRefBytes[0] = (byte)(newRef + 139); + for (int i = 0; i < operatorCode.length; i++) { + newRefBytes[1 + i] = (byte)operatorCode[i]; + } + } else if ((forceLength == -1 && newRef <= 1131) || forceLength == 2) { + newRefBytes = new byte[2 + sizeOfOperator]; + if (newRef <= 363) { + newRefBytes[0] = (byte)247; + } else if (newRef <= 619) { + newRefBytes[0] = (byte)248; + } else if (newRef <= 875) { + newRefBytes[0] = (byte)249; + } else { + newRefBytes[0] = (byte)250; + } + newRefBytes[1] = (byte)(newRef - 108); + for (int i = 0; i < operatorCode.length; i++) { + newRefBytes[2 + i] = (byte)operatorCode[i]; + } + } else if ((forceLength == -1 && newRef <= 32767) || forceLength == 3) { + newRefBytes = new byte[3 + sizeOfOperator]; + newRefBytes[0] = 28; + newRefBytes[1] = (byte)(newRef >> 8); + newRefBytes[2] = (byte)newRef; + for (int i = 0; i < operatorCode.length; i++) { + newRefBytes[3 + i] = (byte)operatorCode[i]; + } + } else { + newRefBytes = new byte[5 + sizeOfOperator]; + newRefBytes[0] = 29; + newRefBytes[1] = (byte)(newRef >> 24); + newRefBytes[2] = (byte)(newRef >> 16); + newRefBytes[3] = (byte)(newRef >> 8); + newRefBytes[4] = (byte)newRef; + for (int i = 0; i < operatorCode.length; i++) { + newRefBytes[5 + i] = (byte)operatorCode[i]; + } + } + return newRefBytes; + } + + public static byte[] concatArray(byte[] a, byte[] b) { + int aLen = a.length; + int bLen = b.length; + byte[] c = new byte[aLen + bLen]; + System.arraycopy(a, 0, c, 0, aLen); + System.arraycopy(b, 0, c, aLen, bLen); + return c; + } + + private int writeIndex(List dataArray) { + int hdrTotal = 3; + //2 byte number of items + this.writeCard16(dataArray.size()); + //Offset Size: 1 byte = 256, 2 bytes = 65536 etc. + int totLength = 0; + for (int i = 0; i < dataArray.size(); i++) { + totLength += dataArray.get(i).length; + } + int offSize = 1; + if (totLength <= (1 << 8)) { + offSize = 1; + } else if (totLength <= (1 << 16)) { + offSize = 2; + } else if (totLength <= (1 << 24)) { + offSize = 3; + } else { + offSize = 4; + } + this.writeByte(offSize); + //Count the first offset 1 + hdrTotal += offSize; + int total = 0; + for (int i = 0; i < dataArray.size(); i++) { + hdrTotal += offSize; + int length = dataArray.get(i).length; + switch (offSize) { + case 1: + if (i == 0) { + writeByte(1); + } + total += length; + writeByte(total + 1); + break; + case 2: + if (i == 0) { + writeCard16(1); + } + total += length; + writeCard16(total + 1); + break; + case 3: + if (i == 0) { + writeThreeByteNumber(1); + } + total += length; + writeThreeByteNumber(total + 1); + break; + case 4: + if (i == 0) { + writeULong(1); + } + total += length; + writeULong(total + 1); + break; + default: + throw new AssertionError("Offset Size was not an expected value."); + } + } + for (int i = 0; i < dataArray.size(); i++) { + writeBytes(dataArray.get(i)); + } + return hdrTotal + total; + } + + + private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException { + if (b0 == 28) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + return new BytesNumber(Integer.valueOf((short) (b1 << 8 | b2)), 3); + } else if (b0 >= 32 && b0 <= 246) { + return new BytesNumber(Integer.valueOf(b0 - 139), 1); + } else if (b0 >= 247 && b0 <= 250) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber(Integer.valueOf((b0 - 247) * 256 + b1 + 108), 2); + } else if (b0 >= 251 && b0 <= 254) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber(Integer.valueOf(-(b0 - 251) * 256 - b1 - 108), 2); + } else if (b0 == 255) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * A class used to store the last number operand and also it's size in bytes + */ + private static final class BytesNumber { + private int number; + private int numBytes; + + public BytesNumber(int number, int numBytes) { + this.number = number; + this.numBytes = numBytes; + } + + public int getNumber() { + return this.number; + } + + public int getNumBytes() { + return this.numBytes; + } + + public void clearNumber() { + this.number = -1; + this.numBytes = -1; + } + } + + private void writeCharsetTable(boolean cidFont) throws IOException { + writeByte(0); + for (int gid : gidToSID.keySet()) { + if (cidFont && gid == 0) { + continue; + } + writeCard16((cidFont) ? gid : gidToSID.get(gid)); + } + } + + private void writePrivateDict() throws IOException { + Map topDICT = cffReader.getTopDictEntries(); + + DICTEntry privateEntry = topDICT.get("Private"); + if (privateEntry != null) { + writeBytes(cffReader.getPrivateDictBytes(privateEntry)); + } + } + + private void updateOffsets(int topDictOffset, int charsetOffset, int charStringOffset, + int privateDictOffset, int localIndexOffset, int encodingOffset) + throws IOException { + Map topDICT = cffReader.getTopDictEntries(); + Map privateDICT = null; + + DICTEntry privateEntry = topDICT.get("Private"); + if (privateEntry != null) { + privateDICT = cffReader.getPrivateDict(privateEntry); + } + + int dataPos = 3 + (cffReader.getTopDictIndex().getOffSize() + * cffReader.getTopDictIndex().getOffsets().length); + int dataTopDictOffset = topDictOffset + dataPos; + + updateFixedOffsets(topDICT, dataTopDictOffset, charsetOffset, charStringOffset, encodingOffset); + + if (privateDICT != null) { + //Private index offset in the top dict + int oldPrivateOffset = dataTopDictOffset + privateEntry.getOffset(); + output = updateOffset(output, oldPrivateOffset + privateEntry.getOperandLengths().get(0), + privateEntry.getOperandLengths().get(1), privateDictOffset); + + //Update the local subroutine index offset in the private dict + DICTEntry subroutines = privateDICT.get("Subrs"); + int oldLocalSubrOffset = privateDictOffset + subroutines.getOffset(); + //Value needs to be converted to -139 etc. + int encodeValue = 0; + if (subroutines.getOperandLength() == 1) { + encodeValue = 139; + } + output = updateOffset(output, oldLocalSubrOffset, subroutines.getOperandLength(), + (localIndexOffset - privateDictOffset) + encodeValue); + } + } + + private void updateFixedOffsets(Map topDICT, int dataTopDictOffset, + int charsetOffset, int charStringOffset, int encodingOffset) { + //Charset offset in the top dict + DICTEntry charset = topDICT.get("charset"); + int oldCharsetOffset = dataTopDictOffset + charset.getOffset(); + output = updateOffset(output, oldCharsetOffset, charset.getOperandLength(), charsetOffset); + + //Char string index offset in the private dict + DICTEntry charString = topDICT.get("CharStrings"); + int oldCharStringOffset = dataTopDictOffset + charString.getOffset(); + output = updateOffset(output, oldCharStringOffset, charString.getOperandLength(), charStringOffset); + + DICTEntry encodingEntry = topDICT.get("Encoding"); + if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0 + && encodingEntry.getOperands().get(0).intValue() != 1) { + int oldEncodingOffset = dataTopDictOffset + encodingEntry.getOffset(); + output = updateOffset(output, oldEncodingOffset, encodingEntry.getOperandLength(), encodingOffset); + } + } + + private void updateCIDOffsets(int topDictDataOffset, int fdArrayOffset, int fdSelectOffset, + int charsetOffset, int charStringOffset, int encodingOffset) { + LinkedHashMap topDict = cffReader.getTopDictEntries(); + + DICTEntry fdArrayEntry = topDict.get("FDArray"); + if (fdArrayEntry != null) { + output = updateOffset(output, topDictDataOffset + fdArrayEntry.getOffset() - 1, + fdArrayEntry.getOperandLength(), fdArrayOffset); + } + + DICTEntry fdSelect = topDict.get("FDSelect"); + if (fdSelect != null) { + output = updateOffset(output, topDictDataOffset + fdSelect.getOffset() - 1, + fdSelect.getOperandLength(), fdSelectOffset); + } + + updateFixedOffsets(topDict, topDictDataOffset, charsetOffset, charStringOffset, encodingOffset); + } + + private byte[] updateOffset(byte[] out, int position, int length, int replacement) { + switch (length) { + case 1: + out[position] = (byte)(replacement & 0xFF); + break; + case 2: + if (replacement <= 363) { + out[position] = (byte)247; + } else if (replacement <= 619) { + out[position] = (byte)248; + } else if (replacement <= 875) { + out[position] = (byte)249; + } else { + out[position] = (byte)250; + } + out[position + 1] = (byte)(replacement - 108); + break; + case 3: + out[position] = (byte)28; + out[position + 1] = (byte)((replacement >> 8) & 0xFF); + out[position + 2] = (byte)(replacement & 0xFF); + break; + case 5: + out[position] = (byte)29; + out[position + 1] = (byte)((replacement >> 24) & 0xFF); + out[position + 2] = (byte)((replacement >> 16) & 0xFF); + out[position + 3] = (byte)((replacement >> 8) & 0xFF); + out[position + 4] = (byte)(replacement & 0xFF); + break; + default: + } + return out; + } + + /** + * Appends a byte to the output array, + * updates currentPost but not realSize + */ + private void writeByte(int b) { + output[currentPos++] = (byte)b; + realSize++; + } + + /** + * Appends a USHORT to the output array, + * updates currentPost but not realSize + */ + private void writeCard16(int s) { + byte b1 = (byte)((s >> 8) & 0xff); + byte b2 = (byte)(s & 0xff); + writeByte(b1); + writeByte(b2); + } + + private void writeThreeByteNumber(int s) { + byte b1 = (byte)((s >> 16) & 0xFF); + byte b2 = (byte)((s >> 8) & 0xFF); + byte b3 = (byte)(s & 0xFF); + output[currentPos++] = b1; + output[currentPos++] = b2; + output[currentPos++] = b3; + realSize += 3; + } + + /** + * Appends a ULONG to the output array, + * at the given position + */ + private void writeULong(int s) { + byte b1 = (byte)((s >> 24) & 0xff); + byte b2 = (byte)((s >> 16) & 0xff); + byte b3 = (byte)((s >> 8) & 0xff); + byte b4 = (byte)(s & 0xff); + output[currentPos++] = b1; + output[currentPos++] = b2; + output[currentPos++] = b3; + output[currentPos++] = b4; + realSize += 4; + } + + /** + * Returns a subset of the fonts (readFont() MUST be called first in order to create the + * subset). + * @return byte array + */ + public byte[] getFontSubset() { + byte[] ret = new byte[realSize]; + System.arraycopy(output, 0, ret, 0, realSize); + return ret; + } +} diff --git a/src/java/org/apache/fop/fonts/truetype/OpenFont.java b/src/java/org/apache/fop/fonts/truetype/OpenFont.java new file mode 100644 index 000000000..ce9a2b388 --- /dev/null +++ b/src/java/org/apache/fop/fonts/truetype/OpenFont.java @@ -0,0 +1,1937 @@ +/* + * 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.truetype; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.xmlgraphics.fonts.Glyphs; + +import org.apache.fop.complexscripts.fonts.AdvancedTypographicTableFormatException; +import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable; +import org.apache.fop.complexscripts.fonts.GlyphPositioningTable; +import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable; +import org.apache.fop.complexscripts.fonts.OTFAdvancedTypographicTableReader; +import org.apache.fop.fonts.CMapSegment; +import org.apache.fop.fonts.FontUtil; +import org.apache.fop.fonts.MultiByteFont; + +public abstract class OpenFont { + + static final byte NTABS = 24; + static final int MAX_CHAR_CODE = 255; + static final int ENC_BUF_SIZE = 1024; + + private static final String[] MAC_GLYPH_ORDERING = { + /* 0x000 */ + ".notdef", ".null", "nonmarkingreturn", "space", + "exclam", "quotedbl", "numbersign", "dollar", + "percent", "ampersand", "quotesingle", "parenleft", + "parenright", "asterisk", "plus", "comma", + /* 0x010 */ + "hyphen", "period", "slash", "zero", + "one", "two", "three", "four", + "five", "six", "seven", "eight", + "nine", "colon", "semicolon", "less", + /* 0x020 */ + "equal", "greater", "question", "at", + "A", "B", "C", "D", + "E", "F", "G", "H", + "I", "J", "K", "L", + /* 0x030 */ + "M", "N", "O", "P", + "Q", "R", "S", "T", + "U", "V", "W", "X", + "Y", "Z", "bracketleft", "backslash", + /* 0x040 */ + "bracketright", "asciicircum", "underscore", "grave", + "a", "b", "c", "d", + "e", "f", "g", "h", + "i", "j", "k", "l", + /* 0x050 */ + "m", "n", "o", "p", + "q", "r", "s", "t", + "u", "v", "w", "x", + "y", "z", "braceleft", "bar", + /* 0x060 */ + "braceright", "asciitilde", "Adieresis", "Aring", + "Ccedilla", "Eacute", "Ntilde", "Odieresis", + "Udieresis", "aacute", "agrave", "acircumflex", + "adieresis", "atilde", "aring", "ccedilla", + /* 0x070 */ + "eacute", "egrave", "ecircumflex", "edieresis", + "iacute", "igrave", "icircumflex", "idieresis", + "ntilde", "oacute", "ograve", "ocircumflex", + "odieresis", "otilde", "uacute", "ugrave", + /* 0x080 */ + "ucircumflex", "udieresis", "dagger", "degree", + "cent", "sterling", "section", "bullet", + "paragraph", "germandbls", "registered", "copyright", + "trademark", "acute", "dieresis", "notequal", + /* 0x090 */ + "AE", "Oslash", "infinity", "plusminus", + "lessequal", "greaterequal", "yen", "mu", + "partialdiff", "summation", "product", "pi", + "integral", "ordfeminine", "ordmasculine", "Omega", + /* 0x0A0 */ + "ae", "oslash", "questiondown", "exclamdown", + "logicalnot", "radical", "florin", "approxequal", + "Delta", "guillemotleft", "guillemotright", "ellipsis", + "nonbreakingspace", "Agrave", "Atilde", "Otilde", + /* 0x0B0 */ + "OE", "oe", "endash", "emdash", + "quotedblleft", "quotedblright", "quoteleft", "quoteright", + "divide", "lozenge", "ydieresis", "Ydieresis", + "fraction", "currency", "guilsinglleft", "guilsinglright", + /* 0x0C0 */ + "fi", "fl", "daggerdbl", "periodcentered", + "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", + "Ecircumflex", "Aacute", "Edieresis", "Egrave", + "Iacute", "Icircumflex", "Idieresis", "Igrave", + /* 0x0D0 */ + "Oacute", "Ocircumflex", "apple", "Ograve", + "Uacute", "Ucircumflex", "Ugrave", "dotlessi", + "circumflex", "tilde", "macron", "breve", + "dotaccent", "ring", "cedilla", "hungarumlaut", + /* 0x0E0 */ + "ogonek", "caron", "Lslash", "lslash", + "Scaron", "scaron", "Zcaron", "zcaron", + "brokenbar", "Eth", "eth", "Yacute", + "yacute", "Thorn", "thorn", "minus", + /* 0x0F0 */ + "multiply", "onesuperior", "twosuperior", "threesuperior", + "onehalf", "onequarter", "threequarters", "franc", + "Gbreve", "gbreve", "Idotaccent", "Scedilla", + "scedilla", "Cacute", "cacute", "Ccaron", + /* 0x100 */ + "ccaron", "dcroat" + }; + + /** The FontFileReader used to read this TrueType font. */ + protected FontFileReader fontFile; + + /** Set to true to get even more debug output than with level DEBUG */ + public static final boolean TRACE_ENABLED = false; + + private static final String ENCODING = "WinAnsiEncoding"; // Default encoding + + private static final short FIRST_CHAR = 0; + + protected boolean useKerning = false; + private boolean isEmbeddable = true; + private boolean hasSerifs = true; + /** + * Table directory + */ + protected Map dirTabs; + + private Map> kerningTab; // for CIDs + private Map> ansiKerningTab; // For winAnsiEncoding + private List cmaps; + protected List unicodeMappings; + + private int upem; // unitsPerEm from "head" table + private int nhmtx; // Number of horizontal metrics + private PostScriptVersion postScriptVersion; + protected int locaFormat; + /** + * Offset to last loca + */ + protected long lastLoca = 0; + protected int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table) + + /** + * Contains glyph data + */ + protected OFMtxEntry[] mtxTab; // Contains glyph data + + protected String postScriptName = ""; + protected String fullName = ""; + protected String notice = ""; + protected final Set familyNames = new HashSet(); + protected String subFamilyName = ""; + + private long italicAngle = 0; + private long isFixedPitch = 0; + private int fontBBox1 = 0; + private int fontBBox2 = 0; + private int fontBBox3 = 0; + private int fontBBox4 = 0; + private int capHeight = 0; + private int os2CapHeight = 0; + private int xHeight = 0; + private int os2xHeight = 0; + //Effective ascender/descender + private int ascender = 0; + private int descender = 0; + //Ascender/descender from hhea table + private int hheaAscender = 0; + private int hheaDescender = 0; + //Ascender/descender from OS/2 table + private int os2Ascender = 0; + private int os2Descender = 0; + private int usWeightClass = 0; + + private short lastChar = 0; + + private int[] ansiWidth; + private Map> ansiIndex; + + // internal mapping of glyph indexes to unicode indexes + // used for quick mappings in this class + private final Map glyphToUnicodeMap = new HashMap(); + private final Map unicodeToGlyphMap = new HashMap(); + + private boolean isCFF; + + // advanced typographic table support + protected boolean useAdvanced = false; + protected OTFAdvancedTypographicTableReader advancedTableReader; + + /** + * Version of the PostScript table (post) contained in this font. + */ + public static enum PostScriptVersion { + /** PostScript table version 1.0. */ + V1, + /** PostScript table version 2.0. */ + V2, + /** PostScript table version 3.0. */ + V3, + /** Unknown version of the PostScript table. */ + UNKNOWN; + } + + /** + * logging instance + */ + protected Log log = LogFactory.getLog(TTFFile.class); + + public OpenFont() { + this(true, false); + } + + /** + * Constructor + * @param useKerning true if kerning data should be loaded + * @param useAdvanced true if advanced typographic tables should be loaded + */ + public OpenFont(boolean useKerning, boolean useAdvanced) { + this.useKerning = useKerning; + this.useAdvanced = useAdvanced; + } + + /** + * Key-value helper class. + */ + final class UnicodeMapping implements Comparable { + + private final int unicodeIndex; + private final int glyphIndex; + + UnicodeMapping(int glyphIndex, int unicodeIndex) { + this.unicodeIndex = unicodeIndex; + this.glyphIndex = glyphIndex; + glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex)); + unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex)); + } + + /** + * Returns the glyphIndex. + * @return the glyph index + */ + public int getGlyphIndex() { + return glyphIndex; + } + + /** + * Returns the unicodeIndex. + * @return the Unicode index + */ + public int getUnicodeIndex() { + return unicodeIndex; + } + + + /** {@inheritDoc} */ + public int hashCode() { + int hc = unicodeIndex; + hc = 19 * hc + (hc ^ glyphIndex); + return hc; + } + + /** {@inheritDoc} */ + public boolean equals(Object o) { + if (o instanceof UnicodeMapping) { + UnicodeMapping m = (UnicodeMapping) o; + if (unicodeIndex != m.unicodeIndex) { + return false; + } else { + return (glyphIndex == m.glyphIndex); + } + } else { + return false; + } + } + + /** {@inheritDoc} */ + public int compareTo(Object o) { + if (o instanceof UnicodeMapping) { + UnicodeMapping m = (UnicodeMapping) o; + if (unicodeIndex > m.unicodeIndex) { + return 1; + } else if (unicodeIndex < m.unicodeIndex) { + return -1; + } else { + return 0; + } + } else { + return -1; + } + } + } + + /** + * Obtain directory table entry. + * @param name (tag) of entry + * @return a directory table entry or null if none found + */ + public OFDirTabEntry getDirectoryEntry(OFTableName name) { + return dirTabs.get(name); + } + + /** + * Position inputstream to position indicated + * in the dirtab offset + offset + * @param in font file reader + * @param tableName (tag) of table + * @param offset from start of table + * @return true if seek succeeded + * @throws IOException if I/O exception occurs during seek + */ + public boolean seekTab(FontFileReader in, OFTableName tableName, + long offset) throws IOException { + OFDirTabEntry dt = dirTabs.get(tableName); + if (dt == null) { + log.error("Dirtab " + tableName.getName() + " not found."); + return false; + } else { + in.seekSet(dt.getOffset() + offset); + } + return true; + } + + /** + * Convert from truetype unit to pdf unit based on the + * unitsPerEm field in the "head" table + * @param n truetype unit + * @return pdf unit + */ + public int convertTTFUnit2PDFUnit(int n) { + int ret; + if (n < 0) { + long rest1 = n % upem; + long storrest = 1000 * rest1; + long ledd2 = (storrest != 0 ? rest1 / storrest : 0); + ret = -((-1000 * n) / upem - (int)ledd2); + } else { + ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem; + } + + return ret; + } + + /** + * Read the cmap table, + * return false if the table is not present or only unsupported + * tables are present. Currently only unicode cmaps are supported. + * Set the unicodeIndex in the TTFMtxEntries and fills in the + * cmaps vector. + */ + protected boolean readCMAP() throws IOException { + + unicodeMappings = new ArrayList(); + + seekTab(fontFile, OFTableName.CMAP, 2); + int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables + long cmapUniOffset = 0; + long symbolMapOffset = 0; + + if (log.isDebugEnabled()) { + log.debug(numCMap + " cmap tables"); + } + + //Read offset for all tables. We are only interested in the unicode table + for (int i = 0; i < numCMap; i++) { + int cmapPID = fontFile.readTTFUShort(); + int cmapEID = fontFile.readTTFUShort(); + long cmapOffset = fontFile.readTTFLong(); + + if (log.isDebugEnabled()) { + log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID); + } + + if (cmapPID == 3 && cmapEID == 1) { + cmapUniOffset = cmapOffset; + } + if (cmapPID == 3 && cmapEID == 0) { + symbolMapOffset = cmapOffset; + } + } + + if (cmapUniOffset > 0) { + return readUnicodeCmap(cmapUniOffset, 1); + } else if (symbolMapOffset > 0) { + return readUnicodeCmap(symbolMapOffset, 0); + } else { + log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table" + + " not present. Aborting"); + return false; + } + } + + private boolean readUnicodeCmap(long cmapUniOffset, int encodingID) + throws IOException { + //Read CMAP table and correct mtxTab.index + int mtxPtr = 0; + + // Read unicode cmap + seekTab(fontFile, OFTableName.CMAP, cmapUniOffset); + int cmapFormat = fontFile.readTTFUShort(); + /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length + + if (log.isDebugEnabled()) { + log.debug("CMAP format: " + cmapFormat); + } + + if (cmapFormat == 4) { + fontFile.skip(2); // Skip version number + int cmapSegCountX2 = fontFile.readTTFUShort(); + int cmapSearchRange = fontFile.readTTFUShort(); + int cmapEntrySelector = fontFile.readTTFUShort(); + int cmapRangeShift = fontFile.readTTFUShort(); + + if (log.isDebugEnabled()) { + log.debug("segCountX2 : " + cmapSegCountX2); + log.debug("searchRange : " + cmapSearchRange); + log.debug("entrySelector: " + cmapEntrySelector); + log.debug("rangeShift : " + cmapRangeShift); + } + + + int[] cmapEndCounts = new int[cmapSegCountX2 / 2]; + int[] cmapStartCounts = new int[cmapSegCountX2 / 2]; + int[] cmapDeltas = new int[cmapSegCountX2 / 2]; + int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2]; + + for (int i = 0; i < (cmapSegCountX2 / 2); i++) { + cmapEndCounts[i] = fontFile.readTTFUShort(); + } + + fontFile.skip(2); // Skip reservedPad + + for (int i = 0; i < (cmapSegCountX2 / 2); i++) { + cmapStartCounts[i] = fontFile.readTTFUShort(); + } + + for (int i = 0; i < (cmapSegCountX2 / 2); i++) { + cmapDeltas[i] = fontFile.readTTFShort(); + } + + //int startRangeOffset = in.getCurrentPos(); + + for (int i = 0; i < (cmapSegCountX2 / 2); i++) { + cmapRangeOffsets[i] = fontFile.readTTFUShort(); + } + + int glyphIdArrayOffset = fontFile.getCurrentPos(); + + BitSet eightBitGlyphs = new BitSet(256); + + // Insert the unicode id for the glyphs in mtxTab + // and fill in the cmaps ArrayList + for (int i = 0; i < cmapStartCounts.length; i++) { + + if (log.isTraceEnabled()) { + log.trace(i + ": " + cmapStartCounts[i] + + " - " + cmapEndCounts[i]); + } + if (log.isDebugEnabled()) { + if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) { + log.debug("Font contains glyphs in the Unicode private use area: " + + Integer.toHexString(cmapStartCounts[i]) + " - " + + Integer.toHexString(cmapEndCounts[i])); + } + } + + for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) { + + // Update lastChar + if (j < 256 && j > lastChar) { + lastChar = (short)j; + } + + if (j < 256) { + eightBitGlyphs.set(j); + } + + if (mtxPtr < mtxTab.length) { + int glyphIdx; + // the last character 65535 = .notdef + // may have a range offset + if (cmapRangeOffsets[i] != 0 && j != 65535) { + int glyphOffset = glyphIdArrayOffset + + ((cmapRangeOffsets[i] / 2) + + (j - cmapStartCounts[i]) + + (i) + - cmapSegCountX2 / 2) * 2; + fontFile.seekSet(glyphOffset); + glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i]) + & 0xffff; + //mtxTab[glyphIdx].setName(mtxTab[glyphIdx].getName() + " - "+(char)j); + unicodeMappings.add(new UnicodeMapping(glyphIdx, j)); + mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); + + if (encodingID == 0 && j >= 0xF020 && j <= 0xF0FF) { + //Experimental: Mapping 0xF020-0xF0FF to 0x0020-0x00FF + //Tested with Wingdings and Symbol TTF fonts which map their + //glyphs in the region 0xF020-0xF0FF. + int mapped = j - 0xF000; + if (!eightBitGlyphs.get(mapped)) { + //Only map if Unicode code point hasn't been mapped before + unicodeMappings.add(new UnicodeMapping(glyphIdx, mapped)); + mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(mapped)); + } + } + + // Also add winAnsiWidth + List v = ansiIndex.get(new Integer(j)); + if (v != null) { + for (Integer aIdx : v) { + ansiWidth[aIdx.intValue()] + = mtxTab[glyphIdx].getWx(); + + if (log.isTraceEnabled()) { + log.trace("Added width " + + mtxTab[glyphIdx].getWx() + + " uni: " + j + + " ansi: " + aIdx.intValue()); + } + } + } + + if (log.isTraceEnabled()) { + log.trace("Idx: " + + glyphIdx + + " Delta: " + cmapDeltas[i] + + " Unicode: " + j + + " name: " + mtxTab[glyphIdx].getName()); + } + } else { + glyphIdx = (j + cmapDeltas[i]) & 0xffff; + + if (glyphIdx < mtxTab.length) { + mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); + } else { + log.debug("Glyph " + glyphIdx + + " out of range: " + + mtxTab.length); + } + + unicodeMappings.add(new UnicodeMapping(glyphIdx, j)); + if (glyphIdx < mtxTab.length) { + mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); + } else { + log.debug("Glyph " + glyphIdx + + " out of range: " + + mtxTab.length); + } + + // Also add winAnsiWidth + List v = ansiIndex.get(new Integer(j)); + if (v != null) { + for (Integer aIdx : v) { + ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx(); + } + } + + //getLogger().debug("IIdx: " + + // mtxPtr + + // " Delta: " + cmap_deltas[i] + + // " Unicode: " + j + + // " name: " + + // mtxTab[(j+cmap_deltas[i]) & 0xffff].name); + + } + if (glyphIdx < mtxTab.length) { + if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) { + mtxPtr++; + } + } + } + } + } + } else { + log.error("Cmap format not supported: " + cmapFormat); + return false; + } + return true; + } + + private boolean isInPrivateUseArea(int start, int end) { + return (isInPrivateUseArea(start) || isInPrivateUseArea(end)); + } + + private boolean isInPrivateUseArea(int unicode) { + return (unicode >= 0xE000 && unicode <= 0xF8FF); + } + + /** + * + * @return mmtx data + */ + public List getMtx() { + return Collections.unmodifiableList(Arrays.asList(mtxTab)); + } + + /** + * Print first char/last char + */ + private void printMaxMin() { + int min = 255; + int max = 0; + for (int i = 0; i < mtxTab.length; i++) { + if (mtxTab[i].getIndex() < min) { + min = mtxTab[i].getIndex(); + } + if (mtxTab[i].getIndex() > max) { + max = mtxTab[i].getIndex(); + } + } + log.info("Min: " + min); + log.info("Max: " + max); + } + + + /** + * Reads the font using a FontFileReader. + * + * @param in The FontFileReader to use + * @throws IOException In case of an I/O problem + */ + public void readFont(FontFileReader in, String header) throws IOException { + readFont(in, header, (String)null); + } + + /** + * initialize the ansiWidths array (for winAnsiEncoding) + * and fill with the missingwidth + */ + protected void initAnsiWidths() { + ansiWidth = new int[256]; + for (int i = 0; i < 256; i++) { + ansiWidth[i] = mtxTab[0].getWx(); + } + + // Create an index hash to the ansiWidth + // Can't just index the winAnsiEncoding when inserting widths + // same char (eg bullet) is repeated more than one place + ansiIndex = new HashMap>(); + for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) { + Integer ansi = new Integer(i); + Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]); + + List v = ansiIndex.get(uni); + if (v == null) { + v = new ArrayList(); + ansiIndex.put(uni, v); + } + v.add(ansi); + } + } + + /** + * Read the font data. + * If the fontfile is a TrueType Collection (.ttc file) + * the name of the font to read data for must be supplied, + * else the name is ignored. + * + * @param in The FontFileReader to use + * @param name The name of the font + * @return boolean Returns true if the font is valid + * @throws IOException In case of an I/O problem + */ + public boolean readFont(FontFileReader in, String header, String name) throws IOException { + initializeFont(in); + /* + * Check if TrueType collection, and that the name + * exists in the collection + */ + if (!checkTTC(header, name)) { + if (name == null) { + throw new IllegalArgumentException( + "For TrueType collection you must specify which font " + + "to select (-ttcname)"); + } else { + throw new IOException( + "Name does not exist in the TrueType collection: " + name); + } + } + + readDirTabs(); + readFontHeader(); + getNumGlyphs(); + if (log.isDebugEnabled()) { + log.debug("Number of glyphs in font: " + numberOfGlyphs); + } + readHorizontalHeader(); + readHorizontalMetrics(); + initAnsiWidths(); + readPostScript(); + readOS2(); + determineAscDesc(); + + readName(); + boolean pcltFound = readPCLT(); + // Read cmap table and fill in ansiwidths + boolean valid = readCMAP(); + if (!valid) { + return false; + } + + // Create cmaps for bfentries + createCMaps(); + updateBBoxAndOffset(); + + if (useKerning) { + readKerning(); + } + handleCharacterSpacing(in); + + guessVerticalMetricsFromGlyphBBox(); + return true; + } + + /** + * Reads a font. + * + * @param in FontFileReader to read from + * @param name Name to be checked for in the font file + * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and + * new index as (Integer) value) + * @throws IOException in case of an I/O problem + */ + public void readFont(FontFileReader in, String header, MultiByteFont mbfont) throws IOException { + readFont(in, header, mbfont.getTTCName()); + } + + protected abstract void updateBBoxAndOffset() throws IOException; + + protected abstract void readName() throws IOException; + + protected abstract void initializeFont(FontFileReader in) throws IOException; + + protected void handleCharacterSpacing(FontFileReader in) throws IOException { + // Read advanced typographic tables. + if (useAdvanced) { + try { + OTFAdvancedTypographicTableReader atr + = new OTFAdvancedTypographicTableReader(this, in); + atr.readAll(); + this.advancedTableReader = atr; + } catch (AdvancedTypographicTableFormatException e) { + log.warn( + "Encountered format constraint violation in advanced (typographic) table (AT) " + + "in font '" + getFullName() + "', ignoring AT data: " + + e.getMessage() + ); + } + } + + } + + protected void createCMaps() { + cmaps = new ArrayList(); + int unicodeStart; + int glyphStart; + int unicodeEnd; + + Iterator e = unicodeMappings.iterator(); + UnicodeMapping um = e.next(); + UnicodeMapping lastMapping = um; + + unicodeStart = um.getUnicodeIndex(); + glyphStart = um.getGlyphIndex(); + + while (e.hasNext()) { + um = e.next(); + if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex()) + || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) { + unicodeEnd = lastMapping.getUnicodeIndex(); + cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart)); + unicodeStart = um.getUnicodeIndex(); + glyphStart = um.getGlyphIndex(); + } + lastMapping = um; + } + + unicodeEnd = lastMapping.getUnicodeIndex(); + cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart)); + } + + /** + * Returns the PostScript name of the font. + * @return String The PostScript name + */ + public String getPostScriptName() { + if (postScriptName.length() == 0) { + return FontUtil.stripWhiteSpace(getFullName()); + } else { + return postScriptName; + } + } + + PostScriptVersion getPostScriptVersion() { + return postScriptVersion; + } + + /** + * Returns the font family names of the font. + * @return Set The family names (a Set of Strings) + */ + public Set getFamilyNames() { + return familyNames; + } + + /** + * Returns the font sub family name of the font. + * @return String The sub family name + */ + public String getSubFamilyName() { + return subFamilyName; + } + + /** + * Returns the full name of the font. + * @return String The full name + */ + public String getFullName() { + return fullName; + } + + /** + * Returns the name of the character set used. + * @return String The caracter set + */ + public String getCharSetName() { + return ENCODING; + } + + /** + * Returns the CapHeight attribute of the font. + * @return int The CapHeight + */ + public int getCapHeight() { + return convertTTFUnit2PDFUnit(capHeight); + } + + /** + * Returns the XHeight attribute of the font. + * @return int The XHeight + */ + public int getXHeight() { + return convertTTFUnit2PDFUnit(xHeight); + } + + /** + * Returns the number of bytes necessary to pad the currentPosition so that a table begins + * on a 4-byte boundary. + * @param currentPosition the position to pad. + * @return int the number of bytes to pad. + */ + protected int getPadSize(int currentPosition) { + int padSize = 4 - (currentPosition % 4); + return padSize < 4 ? padSize : 0; + } + + /** + * Returns the Flags attribute of the font. + * @return int The Flags + */ + public int getFlags() { + int flags = 32; // Use Adobe Standard charset + if (italicAngle != 0) { + flags |= 64; + } + if (isFixedPitch != 0) { + flags |= 2; + } + if (hasSerifs) { + flags |= 1; + } + return flags; + } + + /** + * Returns the weight class of this font. Valid values are 100, 200....,800, 900. + * @return the weight class value (or 0 if there was no OS/2 table in the font) + */ + public int getWeightClass() { + return this.usWeightClass; + } + + /** + * Returns the StemV attribute of the font. + * @return String The StemV + */ + public String getStemV() { + return "0"; + } + + /** + * Returns the ItalicAngle attribute of the font. + * @return String The ItalicAngle + */ + public String getItalicAngle() { + String ia = Short.toString((short)(italicAngle / 0x10000)); + + // This is the correct italic angle, however only int italic + // angles are supported at the moment so this is commented out. + /* + * if ((italicAngle % 0x10000) > 0 ) + * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000))); + */ + return ia; + } + + /** + * @return int[] The font bbox + */ + public int[] getFontBBox() { + final int[] fbb = new int[4]; + fbb[0] = convertTTFUnit2PDFUnit(fontBBox1); + fbb[1] = convertTTFUnit2PDFUnit(fontBBox2); + fbb[2] = convertTTFUnit2PDFUnit(fontBBox3); + fbb[3] = convertTTFUnit2PDFUnit(fontBBox4); + + return fbb; + } + + /** + * Returns the LowerCaseAscent attribute of the font. + * @return int The LowerCaseAscent + */ + public int getLowerCaseAscent() { + return convertTTFUnit2PDFUnit(ascender); + } + + /** + * Returns the LowerCaseDescent attribute of the font. + * @return int The LowerCaseDescent + */ + public int getLowerCaseDescent() { + return convertTTFUnit2PDFUnit(descender); + } + + /** + * Returns the index of the last character, but this is for WinAnsiEncoding + * only, so the last char is < 256. + * @return short Index of the last character (<256) + */ + public short getLastChar() { + return lastChar; + } + + /** + * Returns the index of the first character. + * @return short Index of the first character + */ + public short getFirstChar() { + return FIRST_CHAR; + } + + /** + * Returns an array of character widths. + * @return int[] The character widths + */ + public int[] getWidths() { + int[] wx = new int[mtxTab.length]; + for (int i = 0; i < wx.length; i++) { + wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx()); + } + + return wx; + } + + /** + * Returns an array (xMin, yMin, xMax, yMax) for a glyph. + * + * @param glyphIndex the index of the glyph + * @return int[] Array defining bounding box. + */ + public int[] getBBox(int glyphIndex) { + int[] bboxInTTFUnits = mtxTab[glyphIndex].getBoundingBox(); + int[] bbox = new int[4]; + for (int i = 0; i < 4; i++) { + bbox[i] = convertTTFUnit2PDFUnit(bboxInTTFUnits[i]); + } + return bbox; + } + + /** + * Returns the width of a given character. + * @param idx Index of the character + * @return int Standard width + */ + public int getCharWidth(int idx) { + return convertTTFUnit2PDFUnit(ansiWidth[idx]); + } + + /** + * Returns the kerning table. + * @return Map The kerning table + */ + public Map> getKerning() { + return kerningTab; + } + + /** + * Returns the ANSI kerning table. + * @return Map The ANSI kerning table + */ + public Map> getAnsiKerning() { + return ansiKerningTab; + } + + /** + * Indicates if the font may be embedded. + * @return boolean True if it may be embedded + */ + public boolean isEmbeddable() { + return isEmbeddable; + } + + /** + * Indicates whether or not the font is an OpenType + * CFF font (rather than a TrueType font). + * @return true if the font is in OpenType CFF format. + */ + public boolean isCFF() { + return this.isCFF; + } + + /** + * Read Table Directory from the current position in the + * FontFileReader and fill the global HashMap dirTabs + * with the table name (String) as key and a TTFDirTabEntry + * as value. + * @throws IOException in case of an I/O problem + */ + protected void readDirTabs() throws IOException { + int sfntVersion = fontFile.readTTFLong(); // TTF_FIXED_SIZE (4 bytes) + switch (sfntVersion) { + case 0x10000: + log.debug("sfnt version: OpenType 1.0"); + break; + case 0x4F54544F: //"OTTO" + this.isCFF = true; + log.debug("sfnt version: OpenType with CFF data"); + break; + case 0x74727565: //"true" + log.debug("sfnt version: Apple TrueType"); + break; + case 0x74797031: //"typ1" + log.debug("sfnt version: Apple Type 1 housed in sfnt wrapper"); + break; + default: + log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion)); + break; + } + int ntabs = fontFile.readTTFUShort(); + fontFile.skip(6); // 3xTTF_USHORT_SIZE + + dirTabs = new HashMap(); + OFDirTabEntry[] pd = new OFDirTabEntry[ntabs]; + log.debug("Reading " + ntabs + " dir tables"); + + for (int i = 0; i < ntabs; i++) { + pd[i] = new OFDirTabEntry(); + String tableName = pd[i].read(fontFile); + dirTabs.put(OFTableName.getValue(tableName), pd[i]); + } + dirTabs.put(OFTableName.TABLE_DIRECTORY, + new OFDirTabEntry(0L, fontFile.getCurrentPos())); + log.debug("dir tables: " + dirTabs.keySet()); + } + + /** + * Read the "head" table, this reads the bounding box and + * sets the upem (unitsPerEM) variable + * @throws IOException in case of an I/O problem + */ + protected void readFontHeader() throws IOException { + seekTab(fontFile, OFTableName.HEAD, 2 * 4 + 2 * 4); + int flags = fontFile.readTTFUShort(); + if (log.isDebugEnabled()) { + log.debug("flags: " + flags + " - " + Integer.toString(flags, 2)); + } + upem = fontFile.readTTFUShort(); + if (log.isDebugEnabled()) { + log.debug("unit per em: " + upem); + } + + fontFile.skip(16); + + fontBBox1 = fontFile.readTTFShort(); + fontBBox2 = fontFile.readTTFShort(); + fontBBox3 = fontFile.readTTFShort(); + fontBBox4 = fontFile.readTTFShort(); + if (log.isDebugEnabled()) { + log.debug("font bbox: xMin=" + fontBBox1 + + " yMin=" + fontBBox2 + + " xMax=" + fontBBox3 + + " yMax=" + fontBBox4); + } + + fontFile.skip(2 + 2 + 2); + + locaFormat = fontFile.readTTFShort(); + } + + /** + * Read the number of glyphs from the "maxp" table + * @throws IOException in case of an I/O problem + */ + protected void getNumGlyphs() throws IOException { + seekTab(fontFile, OFTableName.MAXP, 4); + numberOfGlyphs = fontFile.readTTFUShort(); + } + + + /** + * Read the "hhea" table to find the ascender and descender and + * size of "hmtx" table, as a fixed size font might have only + * one width. + * @throws IOException in case of an I/O problem + */ + protected void readHorizontalHeader() + throws IOException { + seekTab(fontFile, OFTableName.HHEA, 4); + hheaAscender = fontFile.readTTFShort(); + hheaDescender = fontFile.readTTFShort(); + + fontFile.skip(2 + 2 + 3 * 2 + 8 * 2); + nhmtx = fontFile.readTTFUShort(); + + if (log.isDebugEnabled()) { + log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender)); + log.debug("hhea.Descender: " + formatUnitsForDebug(hheaDescender)); + log.debug("Number of horizontal metrics: " + nhmtx); + } + } + + /** + * Read "hmtx" table and put the horizontal metrics + * in the mtxTab array. If the number of metrics is less + * than the number of glyphs (eg fixed size fonts), extend + * the mtxTab array and fill in the missing widths + * @throws IOException in case of an I/O problem + */ + protected void readHorizontalMetrics() + throws IOException { + seekTab(fontFile, OFTableName.HMTX, 0); + + int mtxSize = Math.max(numberOfGlyphs, nhmtx); + mtxTab = new OFMtxEntry[mtxSize]; + + if (log.isTraceEnabled()) { + log.trace("*** Widths array: \n"); + } + for (int i = 0; i < mtxSize; i++) { + mtxTab[i] = new OFMtxEntry(); + } + for (int i = 0; i < nhmtx; i++) { + mtxTab[i].setWx(fontFile.readTTFUShort()); + mtxTab[i].setLsb(fontFile.readTTFUShort()); + + if (log.isTraceEnabled()) { + log.trace(" width[" + i + "] = " + + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";"); + } + } + + if (nhmtx < mtxSize) { + // Fill in the missing widths + int lastWidth = mtxTab[nhmtx - 1].getWx(); + for (int i = nhmtx; i < mtxSize; i++) { + mtxTab[i].setWx(lastWidth); + mtxTab[i].setLsb(fontFile.readTTFUShort()); + } + } + } + + + /** + * Read the "post" table + * containing the PostScript names of the glyphs. + */ + protected void readPostScript() throws IOException { + seekTab(fontFile, OFTableName.POST, 0); + int postFormat = fontFile.readTTFLong(); + italicAngle = fontFile.readTTFULong(); + //underlinePosition + fontFile.readTTFShort(); + //underlineThickness + fontFile.readTTFShort(); + isFixedPitch = fontFile.readTTFULong(); + + //Skip memory usage values + fontFile.skip(4 * 4); + + log.debug("PostScript format: 0x" + Integer.toHexString(postFormat)); + switch (postFormat) { + case 0x00010000: + log.debug("PostScript format 1"); + postScriptVersion = PostScriptVersion.V1; + for (int i = 0; i < MAC_GLYPH_ORDERING.length; i++) { + mtxTab[i].setName(MAC_GLYPH_ORDERING[i]); + } + break; + case 0x00020000: + log.debug("PostScript format 2"); + postScriptVersion = PostScriptVersion.V2; + int numGlyphStrings = 0; + + // Read Number of Glyphs + int l = fontFile.readTTFUShort(); + + // Read indexes + for (int i = 0; i < l; i++) { + mtxTab[i].setIndex(fontFile.readTTFUShort()); + + if (mtxTab[i].getIndex() > 257) { + //Index is not in the Macintosh standard set + numGlyphStrings++; + } + + if (log.isTraceEnabled()) { + log.trace("PostScript index: " + mtxTab[i].getIndexAsString()); + } + } + + // firstChar=minIndex; + String[] psGlyphsBuffer = new String[numGlyphStrings]; + if (log.isDebugEnabled()) { + log.debug("Reading " + numGlyphStrings + + " glyphnames, that are not in the standard Macintosh" + + " set. Total number of glyphs=" + l); + } + for (int i = 0; i < psGlyphsBuffer.length; i++) { + psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte()); + } + + //Set glyph names + for (int i = 0; i < l; i++) { + if (mtxTab[i].getIndex() < MAC_GLYPH_ORDERING.length) { + mtxTab[i].setName(MAC_GLYPH_ORDERING[mtxTab[i].getIndex()]); + } else { + if (!mtxTab[i].isIndexReserved()) { + int k = mtxTab[i].getIndex() - MAC_GLYPH_ORDERING.length; + + if (log.isTraceEnabled()) { + log.trace(k + " i=" + i + " mtx=" + mtxTab.length + + " ps=" + psGlyphsBuffer.length); + } + + mtxTab[i].setName(psGlyphsBuffer[k]); + } + } + } + + break; + case 0x00030000: + // PostScript format 3 contains no glyph names + log.debug("PostScript format 3"); + postScriptVersion = PostScriptVersion.V3; + break; + default: + log.error("Unknown PostScript format: " + postFormat); + postScriptVersion = PostScriptVersion.UNKNOWN; + } + } + + + /** + * Read the "OS/2" table + */ + protected void readOS2() throws IOException { + // Check if font is embeddable + OFDirTabEntry os2Entry = dirTabs.get(OFTableName.OS2); + if (os2Entry != null) { + seekTab(fontFile, OFTableName.OS2, 0); + int version = fontFile.readTTFUShort(); + if (log.isDebugEnabled()) { + log.debug("OS/2 table: version=" + version + + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength()); + } + fontFile.skip(2); //xAvgCharWidth + this.usWeightClass = fontFile.readTTFUShort(); + + // usWidthClass + fontFile.skip(2); + + int fsType = fontFile.readTTFUShort(); + if (fsType == 2) { + isEmbeddable = false; + } else { + isEmbeddable = true; + } + fontFile.skip(11 * 2); + fontFile.skip(10); //panose array + fontFile.skip(4 * 4); //unicode ranges + fontFile.skip(4); + fontFile.skip(3 * 2); + int v; + os2Ascender = fontFile.readTTFShort(); //sTypoAscender + os2Descender = fontFile.readTTFShort(); //sTypoDescender + if (log.isDebugEnabled()) { + log.debug("sTypoAscender: " + os2Ascender + + " -> internal " + convertTTFUnit2PDFUnit(os2Ascender)); + log.debug("sTypoDescender: " + os2Descender + + " -> internal " + convertTTFUnit2PDFUnit(os2Descender)); + } + v = fontFile.readTTFShort(); //sTypoLineGap + if (log.isDebugEnabled()) { + log.debug("sTypoLineGap: " + v); + } + v = fontFile.readTTFUShort(); //usWinAscent + if (log.isDebugEnabled()) { + log.debug("usWinAscent: " + formatUnitsForDebug(v)); + } + v = fontFile.readTTFUShort(); //usWinDescent + if (log.isDebugEnabled()) { + log.debug("usWinDescent: " + formatUnitsForDebug(v)); + } + + //version 1 OS/2 table might end here + if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) { + fontFile.skip(2 * 4); + this.os2xHeight = fontFile.readTTFShort(); //sxHeight + this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight + if (log.isDebugEnabled()) { + log.debug("sxHeight: " + this.os2xHeight); + log.debug("sCapHeight: " + this.os2CapHeight); + } + } + + } else { + isEmbeddable = true; + } + } + + /** + * Read the "PCLT" table to find xHeight and capHeight. + * @throws IOException In case of a I/O problem + */ + protected boolean readPCLT() throws IOException { + OFDirTabEntry dirTab = dirTabs.get(OFTableName.PCLT); + if (dirTab != null) { + fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2); + xHeight = fontFile.readTTFUShort(); + log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight)); + fontFile.skip(2 * 2); + capHeight = fontFile.readTTFUShort(); + log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight)); + fontFile.skip(2 + 16 + 8 + 6 + 1 + 1); + + int serifStyle = fontFile.readTTFUByte(); + serifStyle = serifStyle >> 6; + serifStyle = serifStyle & 3; + if (serifStyle == 1) { + hasSerifs = false; + } else { + hasSerifs = true; + } + return true; + } else { + return false; + } + } + + /** + * Determines the right source for the ascender and descender values. The problem here is + * that the interpretation of these values is not the same for every font. There doesn't seem + * to be a uniform definition of an ascender and a descender. In some fonts + * the hhea values are defined after the Apple interpretation, but not in every font. The + * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the + * baseline so we need values which add up more or less to the "em box". However, due to + * accent modifiers a character can grow beyond the em box. + */ + protected void determineAscDesc() { + int hheaBoxHeight = hheaAscender - hheaDescender; + int os2BoxHeight = os2Ascender - os2Descender; + if (os2Ascender > 0 && os2BoxHeight <= upem) { + ascender = os2Ascender; + descender = os2Descender; + } else if (hheaAscender > 0 && hheaBoxHeight <= upem) { + ascender = hheaAscender; + descender = hheaDescender; + } else { + if (os2Ascender > 0) { + //Fall back to info from OS/2 if possible + ascender = os2Ascender; + descender = os2Descender; + } else { + ascender = hheaAscender; + descender = hheaDescender; + } + } + + if (log.isDebugEnabled()) { + log.debug("Font box height: " + (ascender - descender)); + if (ascender - descender > upem) { + log.debug("Ascender and descender together are larger than the em box."); + } + } + } + + protected void guessVerticalMetricsFromGlyphBBox() { + // Approximate capHeight from height of "H" + // It's most unlikely that a font misses the PCLT table + // This also assumes that postscriptnames exists ("H") + // Should look it up in the cmap (that wouldn't help + // for charsets without H anyway...) + // Same for xHeight with the letter "x" + int localCapHeight = 0; + int localXHeight = 0; + int localAscender = 0; + int localDescender = 0; + for (int i = 0; i < mtxTab.length; i++) { + if ("H".equals(mtxTab[i].getName())) { + localCapHeight = mtxTab[i].getBoundingBox()[3]; + } else if ("x".equals(mtxTab[i].getName())) { + localXHeight = mtxTab[i].getBoundingBox()[3]; + } else if ("d".equals(mtxTab[i].getName())) { + localAscender = mtxTab[i].getBoundingBox()[3]; + } else if ("p".equals(mtxTab[i].getName())) { + localDescender = mtxTab[i].getBoundingBox()[1]; + } else { + // OpenType Fonts with a version 3.0 "post" table don't have glyph names. + // Use Unicode indices instead. + List unicodeIndex = mtxTab[i].getUnicodeIndex(); + if (unicodeIndex.size() > 0) { + //Only the first index is used + char ch = (char)((Integer)unicodeIndex.get(0)).intValue(); + if (ch == 'H') { + localCapHeight = mtxTab[i].getBoundingBox()[3]; + } else if (ch == 'x') { + localXHeight = mtxTab[i].getBoundingBox()[3]; + } else if (ch == 'd') { + localAscender = mtxTab[i].getBoundingBox()[3]; + } else if (ch == 'p') { + localDescender = mtxTab[i].getBoundingBox()[1]; + } + } + } + } + if (log.isDebugEnabled()) { + log.debug("Ascender from glyph 'd': " + formatUnitsForDebug(localAscender)); + log.debug("Descender from glyph 'p': " + formatUnitsForDebug(localDescender)); + } + if (ascender - descender > upem) { + log.debug("Replacing specified ascender/descender with derived values to get values" + + " which fit in the em box."); + ascender = localAscender; + descender = localDescender; + } + + if (log.isDebugEnabled()) { + log.debug("xHeight from glyph 'x': " + formatUnitsForDebug(localXHeight)); + log.debug("CapHeight from glyph 'H': " + formatUnitsForDebug(localCapHeight)); + } + if (capHeight == 0) { + capHeight = localCapHeight; + if (capHeight == 0) { + capHeight = os2CapHeight; + } + if (capHeight == 0) { + log.debug("capHeight value could not be determined." + + " The font may not work as expected."); + } + } + if (xHeight == 0) { + xHeight = localXHeight; + if (xHeight == 0) { + xHeight = os2xHeight; + } + if (xHeight == 0) { + log.debug("xHeight value could not be determined." + + " The font may not work as expected."); + } + } + } + + /** + * Read the kerning table, create a table for both CIDs and + * winAnsiEncoding. + * @throws IOException In case of a I/O problem + */ + protected void readKerning() throws IOException { + // Read kerning + kerningTab = new HashMap>(); + ansiKerningTab = new HashMap>(); + OFDirTabEntry dirTab = dirTabs.get(OFTableName.KERN); + if (dirTab != null) { + seekTab(fontFile, OFTableName.KERN, 2); + for (int n = fontFile.readTTFUShort(); n > 0; n--) { + fontFile.skip(2 * 2); + int k = fontFile.readTTFUShort(); + if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) { + return; + } + if ((k >> 8) != 0) { + continue; + } + + k = fontFile.readTTFUShort(); + fontFile.skip(3 * 2); + while (k-- > 0) { + int i = fontFile.readTTFUShort(); + int j = fontFile.readTTFUShort(); + int kpx = fontFile.readTTFShort(); + if (kpx != 0) { + // CID kerning table entry, using unicode indexes + final Integer iObj = glyphToUnicode(i); + final Integer u2 = glyphToUnicode(j); + if (iObj == null) { + // happens for many fonts (Ubuntu font set), + // stray entries in the kerning table?? + log.debug("Ignoring kerning pair because no Unicode index was" + + " found for the first glyph " + i); + } else if (u2 == null) { + log.debug("Ignoring kerning pair because Unicode index was" + + " found for the second glyph " + i); + } else { + Map adjTab = kerningTab.get(iObj); + if (adjTab == null) { + adjTab = new HashMap(); + } + adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx))); + kerningTab.put(iObj, adjTab); + } + } + } + } + + // Create winAnsiEncoded kerning table from kerningTab + // (could probably be simplified, for now we remap back to CID indexes and + // then to winAnsi) + for (Integer unicodeKey1 : kerningTab.keySet()) { + Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue()); + Map akpx = new HashMap(); + Map ckpx = kerningTab.get(unicodeKey1); + + for (Integer unicodeKey2 : ckpx.keySet()) { + Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue()); + Integer kern = ckpx.get(unicodeKey2); + + Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator(); + while (uniMap.hasNext()) { + Integer unicodeKey = (Integer)uniMap.next(); + Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); + for (int u = 0; u < ansiKeys.length; u++) { + akpx.put(ansiKeys[u], kern); + } + } + } + + if (akpx.size() > 0) { + Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator(); + while (uniMap.hasNext()) { + Integer unicodeKey = (Integer)uniMap.next(); + Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); + for (int u = 0; u < ansiKeys.length; u++) { + ansiKerningTab.put(ansiKeys[u], akpx); + } + } + } + } + } + } + + /** + * Streams a font. + * @param ttfOut The interface for streaming TrueType tables. + * @exception IOException file write error + */ + public void stream(TTFOutputStream ttfOut) throws IOException { + SortedSet> sortedDirTabs = sortDirTabMap(dirTabs); + byte[] file = fontFile.getAllBytes(); + TTFTableOutputStream tableOut = ttfOut.getTableOutputStream(); + TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream(); + ttfOut.startFontStream(); + for (Map.Entry entry : sortedDirTabs) { + int offset = (int) entry.getValue().getOffset(); + int paddedLength = (int) entry.getValue().getLength(); + paddedLength += getPadSize(offset + paddedLength); + if (entry.getKey().equals(OFTableName.GLYF)) { + streamGlyf(glyphOut, file, offset, paddedLength); + } else { + tableOut.streamTable(file, offset, paddedLength); + } + } + ttfOut.endFontStream(); + } + + private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset, + int tableLength) throws IOException { + //Stream all but the last glyph + int glyphStart = 0; + int glyphEnd = 0; + glyphOut.startGlyphStream(); + for (int i = 0; i < mtxTab.length - 1; i++) { + glyphStart = (int) mtxTab[i].getOffset() + tableOffset; + glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset; + glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart); + } + glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd); + glyphOut.endGlyphStream(); + } + + /** + * Returns the order in which the tables in a TrueType font should be written to file. + * @param directoryTabs the map that is to be sorted. + * @return TTFTablesNames[] an array of table names sorted in the order they should appear in + * the TTF file. + */ + SortedSet> + sortDirTabMap(Map directoryTabs) { + SortedSet> sortedSet + = new TreeSet>( + new Comparator>() { + + public int compare(Entry o1, + Entry o2) { + return (int) (o1.getValue().getOffset() - o2.getValue().getOffset()); + } + }); + sortedSet.addAll(directoryTabs.entrySet()); + return sortedSet; + } + + /** + * Returns this font's character to glyph mapping. + * + * @return the font's cmap + */ + public List getCMaps() { + return cmaps; + } + + /** + * Check if this is a TrueType collection and that the given + * name exists in the collection. + * If it does, set offset in fontfile to the beginning of + * the Table Directory for that font. + * @param name The name to check + * @return True if not collection or font name present, false otherwise + * @throws IOException In case of an I/O problem + */ + protected final boolean checkTTC(String tag, String name) throws IOException { + if ("ttcf".equals(tag)) { + // This is a TrueType Collection + fontFile.skip(4); + + // Read directory offsets + int numDirectories = (int)fontFile.readTTFULong(); + // int numDirectories=in.readTTFUShort(); + long[] dirOffsets = new long[numDirectories]; + for (int i = 0; i < numDirectories; i++) { + dirOffsets[i] = fontFile.readTTFULong(); + } + + log.info("This is a TrueType collection file with " + + numDirectories + " fonts"); + log.info("Containing the following fonts: "); + // Read all the directories and name tables to check + // If the font exists - this is a bit ugly, but... + boolean found = false; + + // Iterate through all name tables even if font + // Is found, just to show all the names + long dirTabOffset = 0; + for (int i = 0; (i < numDirectories); i++) { + fontFile.seekSet(dirOffsets[i]); + readDirTabs(); + + readName(); + + if (fullName.equals(name)) { + found = true; + dirTabOffset = dirOffsets[i]; + log.info(fullName + " <-- selected"); + } else { + log.info(fullName); + } + + // Reset names + notice = ""; + fullName = ""; + familyNames.clear(); + postScriptName = ""; + subFamilyName = ""; + } + + fontFile.seekSet(dirTabOffset); + return found; + } else { + fontFile.seekSet(0); + return true; + } + } + + /** + * Return TTC font names + * @param in FontFileReader to read from + * @return True if not collection or font name present, false otherwise + * @throws IOException In case of an I/O problem + */ + public final List getTTCnames(FontFileReader in) throws IOException { + this.fontFile = in; + + List fontNames = new ArrayList(); + String tag = in.readTTFString(4); + + if ("ttcf".equals(tag)) { + // This is a TrueType Collection + in.skip(4); + + // Read directory offsets + int numDirectories = (int)in.readTTFULong(); + long[] dirOffsets = new long[numDirectories]; + for (int i = 0; i < numDirectories; i++) { + dirOffsets[i] = in.readTTFULong(); + } + + log.info("This is a TrueType collection file with " + + numDirectories + " fonts"); + log.info("Containing the following fonts: "); + + for (int i = 0; (i < numDirectories); i++) { + in.seekSet(dirOffsets[i]); + readDirTabs(); + + readName(); + + log.info(fullName); + fontNames.add(fullName); + + // Reset names + notice = ""; + fullName = ""; + familyNames.clear(); + postScriptName = ""; + subFamilyName = ""; + } + + in.seekSet(0); + return fontNames; + } else { + log.error("Not a TTC!"); + return null; + } + } + + /* + * Helper classes, they are not very efficient, but that really + * doesn't matter... + */ + private Integer[] unicodeToWinAnsi(int unicode) { + List ret = new ArrayList(); + for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) { + if (unicode == Glyphs.WINANSI_ENCODING[i]) { + ret.add(new Integer(i)); + } + } + return ret.toArray(new Integer[0]); + } + + /** + * Dumps a few informational values to System.out. + */ + public void printStuff() { + System.out.println("Font name: " + postScriptName); + System.out.println("Full name: " + fullName); + System.out.println("Family name: " + familyNames); + System.out.println("Subfamily name: " + subFamilyName); + System.out.println("Notice: " + notice); + System.out.println("xHeight: " + convertTTFUnit2PDFUnit(xHeight)); + System.out.println("capheight: " + convertTTFUnit2PDFUnit(capHeight)); + + int italic = (int)(italicAngle >> 16); + System.out.println("Italic: " + italic); + System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000)); + if ((italicAngle % 0x10000) > 0) { + System.out.print("." + + (short)((italicAngle % 0x10000) * 1000) + / 0x10000); + } + System.out.println(); + System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender)); + System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender)); + System.out.println("FontBBox: [" + convertTTFUnit2PDFUnit(fontBBox1) + + " " + convertTTFUnit2PDFUnit(fontBBox2) + " " + + convertTTFUnit2PDFUnit(fontBBox3) + " " + + convertTTFUnit2PDFUnit(fontBBox4) + "]"); + } + + private String formatUnitsForDebug(int units) { + return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units"; + } + + /** + * Map a glyph index to the corresponding unicode code point + * + * @param glyphIndex + * @return unicode code point + */ + private Integer glyphToUnicode(int glyphIndex) { + return glyphToUnicodeMap.get(new Integer(glyphIndex)); + } + + /** + * Map a unicode code point to the corresponding glyph index + * + * @param unicodeIndex unicode code point + * @return glyph index + */ + private Integer unicodeToGlyph(int unicodeIndex) throws IOException { + final Integer result + = unicodeToGlyphMap.get(new Integer(unicodeIndex)); + if (result == null) { + throw new IOException( + "Glyph index not found for unicode value " + unicodeIndex); + } + return result; + } + + String getGlyphName(int glyphIndex) { + return mtxTab[glyphIndex].getName(); + } + + /** + * Determine if advanced (typographic) table is present. + * @return true if advanced (typographic) table is present + */ + public boolean hasAdvancedTable() { + if (advancedTableReader != null) { + return advancedTableReader.hasAdvancedTable(); + } else { + return false; + } + } + + /** + * Returns the GDEF table or null if none present. + * @return the GDEF table + */ + public GlyphDefinitionTable getGDEF() { + if (advancedTableReader != null) { + return advancedTableReader.getGDEF(); + } else { + return null; + } + } + + /** + * Returns the GSUB table or null if none present. + * @return the GSUB table + */ + public GlyphSubstitutionTable getGSUB() { + if (advancedTableReader != null) { + return advancedTableReader.getGSUB(); + } else { + return null; + } + } + + /** + * Returns the GPOS table or null if none present. + * @return the GPOS table + */ + public GlyphPositioningTable getGPOS() { + if (advancedTableReader != null) { + return advancedTableReader.getGPOS(); + } else { + return null; + } + } + + /** + * Static main method to get info about a TrueType font. + * @param args The command line arguments + */ + public static void main(String[] args) { + InputStream stream = null; + try { + boolean useKerning = true; + boolean useAdvanced = true; + + stream = new FileInputStream(args[0]); + FontFileReader reader = new FontFileReader(stream); + + String name = null; + if (args.length >= 2) { + name = args[1]; + } + + String header = OFFontLoader.readHeader(reader); + boolean isCFF = header.equals("OTTO"); + OpenFont otfFile = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced); + otfFile.readFont(reader, header, name); + otfFile.printStuff(); + + } catch (IOException ioe) { + System.err.println("Problem reading font: " + ioe.toString()); + ioe.printStackTrace(System.err); + } finally { + IOUtils.closeQuietly(stream); + } + } +} diff --git a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java deleted file mode 100644 index c273d4471..000000000 --- a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java +++ /dev/null @@ -1,119 +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.fonts.truetype; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; - - -/** - * This class represents an entry to a TrueType font's Dir Tab. - */ -public class TTFDirTabEntry { - - private byte[] tag = new byte[4]; - private int checksum; - private long offset; - private long length; - - public TTFDirTabEntry() { - } - - public TTFDirTabEntry(long offset, long length) { - this.offset = offset; - this.length = length; - } - - /** - * Read Dir Tab. - * @param in font file reader - * @return tag name - * @throws IOException upon I/O exception - */ - public String read(FontFileReader in) throws IOException { - tag[0] = in.readTTFByte(); - tag[1] = in.readTTFByte(); - tag[2] = in.readTTFByte(); - tag[3] = in.readTTFByte(); - - in.skip(4); // Skip checksum - - offset = in.readTTFULong(); - length = in.readTTFULong(); - String tagStr = new String(tag, "ISO-8859-1"); - - return tagStr; - } - - - @Override - public String toString() { - return "Read dir tab [" - + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]" - + " offset: " + offset - + " length: " + length - + " name: " + tag; - } - - /** - * Returns the checksum. - * @return int - */ - public int getChecksum() { - return checksum; - } - - /** - * Returns the length. - * @return long - */ - public long getLength() { - return length; - } - - /** - * Returns the offset. - * @return long - */ - public long getOffset() { - return offset; - } - - /** - * Returns the tag bytes. - * @return byte[] - */ - public byte[] getTag() { - return tag; - } - - /** - * Returns the tag bytes. - * @return byte[] - */ - public String getTagString() { - try { - return new String(tag, "ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - return this.toString(); // Should never happen. - } - } - -} diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java index 2e78ef7f0..52df45ffb 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java @@ -19,1409 +19,34 @@ package org.apache.fop.fonts.truetype; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.xmlgraphics.fonts.Glyphs; - -import org.apache.fop.complexscripts.fonts.AdvancedTypographicTableFormatException; -import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable; -import org.apache.fop.complexscripts.fonts.GlyphPositioningTable; -import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable; -import org.apache.fop.complexscripts.fonts.OTFAdvancedTypographicTableReader; -import org.apache.fop.fonts.CMapSegment; -import org.apache.fop.fonts.FontUtil; - -/** - * Reads a TrueType file or a TrueType Collection. - * The TrueType spec can be found at the Microsoft. - * Typography site: http://www.microsoft.com/truetype/ - */ -public class TTFFile { - - static final byte NTABS = 24; - static final int MAX_CHAR_CODE = 255; - static final int ENC_BUF_SIZE = 1024; - - private static final String[] MAC_GLYPH_ORDERING = { - /* 0x000 */ - ".notdef", ".null", "nonmarkingreturn", "space", - "exclam", "quotedbl", "numbersign", "dollar", - "percent", "ampersand", "quotesingle", "parenleft", - "parenright", "asterisk", "plus", "comma", - /* 0x010 */ - "hyphen", "period", "slash", "zero", - "one", "two", "three", "four", - "five", "six", "seven", "eight", - "nine", "colon", "semicolon", "less", - /* 0x020 */ - "equal", "greater", "question", "at", - "A", "B", "C", "D", - "E", "F", "G", "H", - "I", "J", "K", "L", - /* 0x030 */ - "M", "N", "O", "P", - "Q", "R", "S", "T", - "U", "V", "W", "X", - "Y", "Z", "bracketleft", "backslash", - /* 0x040 */ - "bracketright", "asciicircum", "underscore", "grave", - "a", "b", "c", "d", - "e", "f", "g", "h", - "i", "j", "k", "l", - /* 0x050 */ - "m", "n", "o", "p", - "q", "r", "s", "t", - "u", "v", "w", "x", - "y", "z", "braceleft", "bar", - /* 0x060 */ - "braceright", "asciitilde", "Adieresis", "Aring", - "Ccedilla", "Eacute", "Ntilde", "Odieresis", - "Udieresis", "aacute", "agrave", "acircumflex", - "adieresis", "atilde", "aring", "ccedilla", - /* 0x070 */ - "eacute", "egrave", "ecircumflex", "edieresis", - "iacute", "igrave", "icircumflex", "idieresis", - "ntilde", "oacute", "ograve", "ocircumflex", - "odieresis", "otilde", "uacute", "ugrave", - /* 0x080 */ - "ucircumflex", "udieresis", "dagger", "degree", - "cent", "sterling", "section", "bullet", - "paragraph", "germandbls", "registered", "copyright", - "trademark", "acute", "dieresis", "notequal", - /* 0x090 */ - "AE", "Oslash", "infinity", "plusminus", - "lessequal", "greaterequal", "yen", "mu", - "partialdiff", "summation", "product", "pi", - "integral", "ordfeminine", "ordmasculine", "Omega", - /* 0x0A0 */ - "ae", "oslash", "questiondown", "exclamdown", - "logicalnot", "radical", "florin", "approxequal", - "Delta", "guillemotleft", "guillemotright", "ellipsis", - "nonbreakingspace", "Agrave", "Atilde", "Otilde", - /* 0x0B0 */ - "OE", "oe", "endash", "emdash", - "quotedblleft", "quotedblright", "quoteleft", "quoteright", - "divide", "lozenge", "ydieresis", "Ydieresis", - "fraction", "currency", "guilsinglleft", "guilsinglright", - /* 0x0C0 */ - "fi", "fl", "daggerdbl", "periodcentered", - "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", - "Ecircumflex", "Aacute", "Edieresis", "Egrave", - "Iacute", "Icircumflex", "Idieresis", "Igrave", - /* 0x0D0 */ - "Oacute", "Ocircumflex", "apple", "Ograve", - "Uacute", "Ucircumflex", "Ugrave", "dotlessi", - "circumflex", "tilde", "macron", "breve", - "dotaccent", "ring", "cedilla", "hungarumlaut", - /* 0x0E0 */ - "ogonek", "caron", "Lslash", "lslash", - "Scaron", "scaron", "Zcaron", "zcaron", - "brokenbar", "Eth", "eth", "Yacute", - "yacute", "Thorn", "thorn", "minus", - /* 0x0F0 */ - "multiply", "onesuperior", "twosuperior", "threesuperior", - "onehalf", "onequarter", "threequarters", "franc", - "Gbreve", "gbreve", "Idotaccent", "Scedilla", - "scedilla", "Cacute", "cacute", "Ccaron", - /* 0x100 */ - "ccaron", "dcroat" - }; - - /** The FontFileReader used to read this TrueType font. */ - protected FontFileReader fontFile; - - /** Set to true to get even more debug output than with level DEBUG */ - public static final boolean TRACE_ENABLED = false; - - private final String encoding = "WinAnsiEncoding"; // Default encoding - - private final short firstChar = 0; - - private boolean useKerning = false; - - private boolean isEmbeddable = true; - private boolean hasSerifs = true; - /** - * Table directory - */ - protected Map dirTabs; - private Map> kerningTab; // for CIDs - private Map> ansiKerningTab; // For winAnsiEncoding - private List cmaps; - private Set unicodeMappings; - - private int upem; // unitsPerEm from "head" table - private int nhmtx; // Number of horizontal metrics - private PostScriptVersion postScriptVersion; - private int locaFormat; - /** - * Offset to last loca - */ - protected long lastLoca = 0; - private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table) - - /** - * Contains glyph data - */ - protected TTFMtxEntry[] mtxTab; // Contains glyph data - - private String postScriptName = ""; - private String fullName = ""; - private String notice = ""; - private Set familyNames = new HashSet(); - private String subFamilyName = ""; - - private long italicAngle = 0; - private long isFixedPitch = 0; - private int fontBBox1 = 0; - private int fontBBox2 = 0; - private int fontBBox3 = 0; - private int fontBBox4 = 0; - private int capHeight = 0; - private int os2CapHeight = 0; - private int underlinePosition = 0; - private int underlineThickness = 0; - private int xHeight = 0; - private int os2xHeight = 0; - //Effective ascender/descender - private int ascender = 0; - private int descender = 0; - //Ascender/descender from hhea table - private int hheaAscender = 0; - private int hheaDescender = 0; - //Ascender/descender from OS/2 table - private int os2Ascender = 0; - private int os2Descender = 0; - private int usWeightClass = 0; - - private short lastChar = 0; - - private int[] ansiWidth; - private Map> ansiIndex; - - // internal mapping of glyph indexes to unicode indexes - // used for quick mappings in this class - private final Map glyphToUnicodeMap = new HashMap(); - private final Map unicodeToGlyphMap = new HashMap(); - - private TTFDirTabEntry currentDirTab; - - private boolean isCFF; - - // advanced typographic table support - private boolean useAdvanced = false; - private OTFAdvancedTypographicTableReader advancedTableReader; - - /** - * logging instance - */ - protected Log log = LogFactory.getLog(TTFFile.class); - - public TTFFile() { - this(true, false); - } - - /** - * Constructor - * @param useKerning true if kerning data should be loaded - * @param useAdvanced true if advanced typographic tables should be loaded - */ - public TTFFile(boolean useKerning, boolean useAdvanced) { - this.useKerning = useKerning; - this.useAdvanced = useAdvanced; - } - - /** - * Key-value helper class. - */ - final class UnicodeMapping implements Comparable { - - private final int unicodeIndex; - private final int glyphIndex; - - UnicodeMapping(int glyphIndex, int unicodeIndex) { - this.unicodeIndex = unicodeIndex; - this.glyphIndex = glyphIndex; - glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex)); - unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex)); - } - - /** - * Returns the glyphIndex. - * @return the glyph index - */ - public int getGlyphIndex() { - return glyphIndex; - } - - /** - * Returns the unicodeIndex. - * @return the Unicode index - */ - public int getUnicodeIndex() { - return unicodeIndex; - } - - - /** {@inheritDoc} */ - public int hashCode() { - int hc = unicodeIndex; - hc = 19 * hc + (hc ^ glyphIndex); - return hc; - } - - /** {@inheritDoc} */ - public boolean equals(Object o) { - if (o instanceof UnicodeMapping) { - UnicodeMapping m = (UnicodeMapping) o; - if (unicodeIndex != m.unicodeIndex) { - return false; - } else { - return (glyphIndex == m.glyphIndex); - } - } else { - return false; - } - } - - /** {@inheritDoc} */ - public int compareTo(Object o) { - if (o instanceof UnicodeMapping) { - UnicodeMapping m = (UnicodeMapping) o; - if (unicodeIndex > m.unicodeIndex) { - return 1; - } else if (unicodeIndex < m.unicodeIndex) { - return -1; - } else { - return 0; - } - } else { - return -1; - } - } - } - - /** - * Version of the PostScript table (post) contained in this font. - */ - public static enum PostScriptVersion { - /** PostScript table version 1.0. */ - V1, - /** PostScript table version 2.0. */ - V2, - /** PostScript table version 3.0. */ - V3, - /** Unknown version of the PostScript table. */ - UNKNOWN; - } - - /** - * Obtain directory table entry. - * @param name (tag) of entry - * @return a directory table entry or null if none found - */ - public TTFDirTabEntry getDirectoryEntry(TTFTableName name) { - return dirTabs.get(name); - } - - /** - * Position inputstream to position indicated - * in the dirtab offset + offset - * @param in font file reader - * @param tableName (tag) of table - * @param offset from start of table - * @return true if seek succeeded - * @throws IOException if I/O exception occurs during seek - */ - public boolean seekTab(FontFileReader in, TTFTableName tableName, - long offset) throws IOException { - TTFDirTabEntry dt = dirTabs.get(tableName); - if (dt == null) { - log.error("Dirtab " + tableName.getName() + " not found."); - return false; - } else { - in.seekSet(dt.getOffset() + offset); - this.currentDirTab = dt; - } - return true; - } - - /** - * Convert from truetype unit to pdf unit based on the - * unitsPerEm field in the "head" table - * @param n truetype unit - * @return pdf unit - */ - public int convertTTFUnit2PDFUnit(int n) { - int ret; - if (n < 0) { - long rest1 = n % upem; - long storrest = 1000 * rest1; - long ledd2 = (storrest != 0 ? rest1 / storrest : 0); - ret = -((-1000 * n) / upem - (int)ledd2); - } else { - ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem; - } - - return ret; - } - - /** - * Read the cmap table, - * return false if the table is not present or only unsupported - * tables are present. Currently only unicode cmaps are supported. - * Set the unicodeIndex in the TTFMtxEntries and fills in the - * cmaps vector. - */ - private boolean readCMAP() throws IOException { - - unicodeMappings = new java.util.TreeSet(); - - seekTab(fontFile, TTFTableName.CMAP, 2); - int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables - long cmapUniOffset = 0; - long symbolMapOffset = 0; - - if (log.isDebugEnabled()) { - log.debug(numCMap + " cmap tables"); - } - - //Read offset for all tables. We are only interested in the unicode table - for (int i = 0; i < numCMap; i++) { - int cmapPID = fontFile.readTTFUShort(); - int cmapEID = fontFile.readTTFUShort(); - long cmapOffset = fontFile.readTTFLong(); - - if (log.isDebugEnabled()) { - log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID); - } - - if (cmapPID == 3 && cmapEID == 1) { - cmapUniOffset = cmapOffset; - } - if (cmapPID == 3 && cmapEID == 0) { - symbolMapOffset = cmapOffset; - } - } - - if (cmapUniOffset > 0) { - return readUnicodeCmap(cmapUniOffset, 1); - } else if (symbolMapOffset > 0) { - return readUnicodeCmap(symbolMapOffset, 0); - } else { - log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table" - + " not present. Aborting"); - return false; - } - } - - private boolean readUnicodeCmap( - long cmapUniOffset, int encodingID) - throws IOException { - //Read CMAP table and correct mtxTab.index - int mtxPtr = 0; - - // Read unicode cmap - seekTab(fontFile, TTFTableName.CMAP, cmapUniOffset); - int cmapFormat = fontFile.readTTFUShort(); - /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length - - if (log.isDebugEnabled()) { - log.debug("CMAP format: " + cmapFormat); - } - - if (cmapFormat == 4) { - fontFile.skip(2); // Skip version number - int cmapSegCountX2 = fontFile.readTTFUShort(); - int cmapSearchRange = fontFile.readTTFUShort(); - int cmapEntrySelector = fontFile.readTTFUShort(); - int cmapRangeShift = fontFile.readTTFUShort(); - - if (log.isDebugEnabled()) { - log.debug("segCountX2 : " + cmapSegCountX2); - log.debug("searchRange : " + cmapSearchRange); - log.debug("entrySelector: " + cmapEntrySelector); - log.debug("rangeShift : " + cmapRangeShift); - } - - - int[] cmapEndCounts = new int[cmapSegCountX2 / 2]; - int[] cmapStartCounts = new int[cmapSegCountX2 / 2]; - int[] cmapDeltas = new int[cmapSegCountX2 / 2]; - int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2]; - - for (int i = 0; i < (cmapSegCountX2 / 2); i++) { - cmapEndCounts[i] = fontFile.readTTFUShort(); - } - - fontFile.skip(2); // Skip reservedPad - - for (int i = 0; i < (cmapSegCountX2 / 2); i++) { - cmapStartCounts[i] = fontFile.readTTFUShort(); - } - - for (int i = 0; i < (cmapSegCountX2 / 2); i++) { - cmapDeltas[i] = fontFile.readTTFShort(); - } - - //int startRangeOffset = in.getCurrentPos(); - - for (int i = 0; i < (cmapSegCountX2 / 2); i++) { - cmapRangeOffsets[i] = fontFile.readTTFUShort(); - } - - int glyphIdArrayOffset = fontFile.getCurrentPos(); - - BitSet eightBitGlyphs = new BitSet(256); - - // Insert the unicode id for the glyphs in mtxTab - // and fill in the cmaps ArrayList - - for (int i = 0; i < cmapStartCounts.length; i++) { - - if (log.isTraceEnabled()) { - log.trace(i + ": " + cmapStartCounts[i] - + " - " + cmapEndCounts[i]); - } - if (log.isDebugEnabled()) { - if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) { - log.debug("Font contains glyphs in the Unicode private use area: " - + Integer.toHexString(cmapStartCounts[i]) + " - " - + Integer.toHexString(cmapEndCounts[i])); - } - } - - for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) { - - // Update lastChar - if (j < 256 && j > lastChar) { - lastChar = (short)j; - } - - if (j < 256) { - eightBitGlyphs.set(j); - } - - if (mtxPtr < mtxTab.length) { - int glyphIdx; - // the last character 65535 = .notdef - // may have a range offset - if (cmapRangeOffsets[i] != 0 && j != 65535) { - int glyphOffset = glyphIdArrayOffset - + ((cmapRangeOffsets[i] / 2) - + (j - cmapStartCounts[i]) - + (i) - - cmapSegCountX2 / 2) * 2; - fontFile.seekSet(glyphOffset); - glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i]) - & 0xffff; - - unicodeMappings.add(new UnicodeMapping(glyphIdx, j)); - mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); - - // Also add winAnsiWidth - List v = ansiIndex.get(new Integer(j)); - if (v != null) { - for (Integer aIdx : v) { - ansiWidth[aIdx.intValue()] - = mtxTab[glyphIdx].getWx(); - - if (log.isTraceEnabled()) { - log.trace("Added width " - + mtxTab[glyphIdx].getWx() - + " uni: " + j - + " ansi: " + aIdx.intValue()); - } - } - } - - if (log.isTraceEnabled()) { - log.trace("Idx: " - + glyphIdx - + " Delta: " + cmapDeltas[i] - + " Unicode: " + j - + " name: " + mtxTab[glyphIdx].getName()); - } - } else { - glyphIdx = (j + cmapDeltas[i]) & 0xffff; - - if (glyphIdx < mtxTab.length) { - mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); - } else { - log.debug("Glyph " + glyphIdx - + " out of range: " - + mtxTab.length); - } - - unicodeMappings.add(new UnicodeMapping(glyphIdx, j)); - if (glyphIdx < mtxTab.length) { - mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j)); - } else { - log.debug("Glyph " + glyphIdx - + " out of range: " - + mtxTab.length); - } - - // Also add winAnsiWidth - List v = ansiIndex.get(new Integer(j)); - if (v != null) { - for (Integer aIdx : v) { - ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx(); - } - } - - //getLogger().debug("IIdx: " + - // mtxPtr + - // " Delta: " + cmap_deltas[i] + - // " Unicode: " + j + - // " name: " + - // mtxTab[(j+cmap_deltas[i]) & 0xffff].name); - - } - if (glyphIdx < mtxTab.length) { - if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) { - mtxPtr++; - } - } - } - } - } - } else { - log.error("Cmap format not supported: " + cmapFormat); - return false; - } - return true; - } - - private boolean isInPrivateUseArea(int start, int end) { - return (isInPrivateUseArea(start) || isInPrivateUseArea(end)); - } - - private boolean isInPrivateUseArea(int unicode) { - return (unicode >= 0xE000 && unicode <= 0xF8FF); - } - - /** - * Print first char/last char - */ - private void printMaxMin() { - int min = 255; - int max = 0; - for (int i = 0; i < mtxTab.length; i++) { - if (mtxTab[i].getIndex() < min) { - min = mtxTab[i].getIndex(); - } - if (mtxTab[i].getIndex() > max) { - max = mtxTab[i].getIndex(); - } - } - log.info("Min: " + min); - log.info("Max: " + max); - } - - - /** - * Reads the font using a FontFileReader. - * - * @param in The FontFileReader to use - * @throws IOException In case of an I/O problem - */ - public void readFont(FontFileReader in) throws IOException { - readFont(in, (String)null); - } - - /** - * initialize the ansiWidths array (for winAnsiEncoding) - * and fill with the missingwidth - */ - private void initAnsiWidths() { - ansiWidth = new int[256]; - for (int i = 0; i < 256; i++) { - ansiWidth[i] = mtxTab[0].getWx(); - } - - // Create an index hash to the ansiWidth - // Can't just index the winAnsiEncoding when inserting widths - // same char (eg bullet) is repeated more than one place - ansiIndex = new HashMap>(); - for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) { - Integer ansi = new Integer(i); - Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]); - - List v = ansiIndex.get(uni); - if (v == null) { - v = new ArrayList(); - ansiIndex.put(uni, v); - } - v.add(ansi); - } - } - - /** - * Read the font data. - * If the fontfile is a TrueType Collection (.ttc file) - * the name of the font to read data for must be supplied, - * else the name is ignored. - * - * @param in The FontFileReader to use - * @param name The name of the font - * @return boolean Returns true if the font is valid - * @throws IOException In case of an I/O problem - */ - public boolean readFont(FontFileReader in, String name) throws IOException { - fontFile = in; - /* - * Check if TrueType collection, and that the name - * exists in the collection - */ - if (!checkTTC(name)) { - if (name == null) { - throw new IllegalArgumentException( - "For TrueType collection you must specify which font " - + "to select (-ttcname)"); - } else { - throw new IOException( - "Name does not exist in the TrueType collection: " + name); - } - } - - readDirTabs(); - readFontHeader(); - getNumGlyphs(); - if (log.isDebugEnabled()) { - log.debug("Number of glyphs in font: " + numberOfGlyphs); - } - readHorizontalHeader(); - readHorizontalMetrics(); - initAnsiWidths(); - readPostScript(); - readOS2(); - determineAscDesc(); - if (!isCFF) { - readIndexToLocation(); - readGlyf(); - } - readName(); - boolean pcltFound = readPCLT(); - // Read cmap table and fill in ansiwidths - boolean valid = readCMAP(); - if (!valid) { - return false; - } - // Create cmaps for bfentries - createCMaps(); - - if (useKerning) { - readKerning(); - } - - // Read advanced typographic tables. - if (useAdvanced) { - try { - OTFAdvancedTypographicTableReader atr - = new OTFAdvancedTypographicTableReader(this, in); - atr.readAll(); - this.advancedTableReader = atr; - } catch (AdvancedTypographicTableFormatException e) { - log.warn( - "Encountered format constraint violation in advanced (typographic) table (AT) " - + "in font '" + getFullName() + "', ignoring AT data: " - + e.getMessage() - ); - } - } - - guessVerticalMetricsFromGlyphBBox(); - return true; - } - - /** - * Reads a font. - * - * @param in FontFileReader to read from - * @param name Name to be checked for in the font file - * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and - * new index as (Integer) value) - * @throws IOException in case of an I/O problem - */ - public void readFont(FontFileReader in, String name, - Map glyphs) throws IOException { - readFont(in, name); - } - - private void createCMaps() { - cmaps = new ArrayList(); - int unicodeStart; - int glyphStart; - int unicodeEnd; - - Iterator e = unicodeMappings.iterator(); - UnicodeMapping um = e.next(); - UnicodeMapping lastMapping = um; - - unicodeStart = um.getUnicodeIndex(); - glyphStart = um.getGlyphIndex(); - - while (e.hasNext()) { - um = e.next(); - if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex()) - || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) { - unicodeEnd = lastMapping.getUnicodeIndex(); - cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart)); - unicodeStart = um.getUnicodeIndex(); - glyphStart = um.getGlyphIndex(); - } - lastMapping = um; - } - - unicodeEnd = lastMapping.getUnicodeIndex(); - cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart)); - } - - /** - * Returns the PostScript name of the font. - * @return String The PostScript name - */ - public String getPostScriptName() { - if (postScriptName.length() == 0) { - return FontUtil.stripWhiteSpace(getFullName()); - } else { - return postScriptName; - } - } - - PostScriptVersion getPostScriptVersion() { - return postScriptVersion; - } - - /** - * Returns the font family names of the font. - * @return Set The family names (a Set of Strings) - */ - public Set getFamilyNames() { - return familyNames; - } - - /** - * Returns the font sub family name of the font. - * @return String The sub family name - */ - public String getSubFamilyName() { - return subFamilyName; - } - - /** - * Returns the full name of the font. - * @return String The full name - */ - public String getFullName() { - return fullName; - } - - /** - * Returns the name of the character set used. - * @return String The caracter set - */ - public String getCharSetName() { - return encoding; - } - - /** - * Returns the CapHeight attribute of the font. - * @return int The CapHeight - */ - public int getCapHeight() { - return convertTTFUnit2PDFUnit(capHeight); - } - - /** - * Returns the XHeight attribute of the font. - * @return int The XHeight - */ - public int getXHeight() { - return convertTTFUnit2PDFUnit(xHeight); - } - - /** - * Returns the number of bytes necessary to pad the currentPosition so that a table begins - * on a 4-byte boundary. - * @param currentPosition the position to pad. - * @return int the number of bytes to pad. - */ - protected int getPadSize(int currentPosition) { - int padSize = 4 - (currentPosition % 4); - return padSize < 4 ? padSize : 0; - } - - /** - * Returns the Flags attribute of the font. - * @return int The Flags - */ - public int getFlags() { - int flags = 32; // Use Adobe Standard charset - if (italicAngle != 0) { - flags |= 64; - } - if (isFixedPitch != 0) { - flags |= 2; - } - if (hasSerifs) { - flags |= 1; - } - return flags; - } - - /** - * Returns the weight class of this font. Valid values are 100, 200....,800, 900. - * @return the weight class value (or 0 if there was no OS/2 table in the font) - */ - public int getWeightClass() { - return this.usWeightClass; - } - - /** - * Returns the StemV attribute of the font. - * @return String The StemV - */ - public String getStemV() { - return "0"; - } - - /** - * Returns the ItalicAngle attribute of the font. - * @return String The ItalicAngle - */ - public String getItalicAngle() { - String ia = Short.toString((short)(italicAngle / 0x10000)); - - // This is the correct italic angle, however only int italic - // angles are supported at the moment so this is commented out. - /* - * if ((italicAngle % 0x10000) > 0 ) - * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000))); - */ - return ia; - } - - /** - * @return int[] The font bbox - */ - public int[] getFontBBox() { - final int[] fbb = new int[4]; - fbb[0] = convertTTFUnit2PDFUnit(fontBBox1); - fbb[1] = convertTTFUnit2PDFUnit(fontBBox2); - fbb[2] = convertTTFUnit2PDFUnit(fontBBox3); - fbb[3] = convertTTFUnit2PDFUnit(fontBBox4); - - return fbb; - } - - /** - * Returns the LowerCaseAscent attribute of the font. - * @return int The LowerCaseAscent - */ - public int getLowerCaseAscent() { - return convertTTFUnit2PDFUnit(ascender); - } - - /** - * Returns the LowerCaseDescent attribute of the font. - * @return int The LowerCaseDescent - */ - public int getLowerCaseDescent() { - return convertTTFUnit2PDFUnit(descender); - } - - /** - * Returns the index of the last character, but this is for WinAnsiEncoding - * only, so the last char is < 256. - * @return short Index of the last character (<256) - */ - public short getLastChar() { - return lastChar; - } - - /** - * Returns the index of the first character. - * @return short Index of the first character - */ - public short getFirstChar() { - return firstChar; - } - - /** - * Returns an array of character widths. - * @return int[] The character widths - */ - public int[] getWidths() { - int[] wx = new int[mtxTab.length]; - for (int i = 0; i < wx.length; i++) { - wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx()); - } - - return wx; - } - - /** - * Returns an array (xMin, yMin, xMax, yMax) for a glyph. - * - * @param glyphIndex the index of the glyph - * @return int[] Array defining bounding box. - */ - public int[] getBBox(int glyphIndex) { - int[] bboxInTTFUnits = mtxTab[glyphIndex].getBoundingBox(); - int[] bbox = new int[4]; - for (int i = 0; i < 4; i++) { - bbox[i] = convertTTFUnit2PDFUnit(bboxInTTFUnits[i]); - } - return bbox; - } - - /** - * Returns the width of a given character. - * @param idx Index of the character - * @return int Standard width - */ - public int getCharWidth(int idx) { - return convertTTFUnit2PDFUnit(ansiWidth[idx]); - } - - /** - * Returns the kerning table. - * @return Map The kerning table - */ - public Map> getKerning() { - return kerningTab; - } - - /** - * Returns the ANSI kerning table. - * @return Map The ANSI kerning table - */ - public Map> getAnsiKerning() { - return ansiKerningTab; - } - - /** - * Indicates if the font may be embedded. - * @return boolean True if it may be embedded - */ - public boolean isEmbeddable() { - return isEmbeddable; - } - - /** - * Indicates whether or not the font is an OpenType - * CFF font (rather than a TrueType font). - * @return true if the font is in OpenType CFF format. - */ - public boolean isCFF() { - return this.isCFF; - } - - /** - * Read Table Directory from the current position in the - * FontFileReader and fill the global HashMap dirTabs - * with the table name (String) as key and a TTFDirTabEntry - * as value. - * @throws IOException in case of an I/O problem - */ - protected void readDirTabs() throws IOException { - int sfntVersion = fontFile.readTTFLong(); // TTF_FIXED_SIZE (4 bytes) - switch (sfntVersion) { - case 0x10000: - log.debug("sfnt version: OpenType 1.0"); - break; - case 0x4F54544F: //"OTTO" - this.isCFF = true; - log.debug("sfnt version: OpenType with CFF data"); - break; - case 0x74727565: //"true" - log.debug("sfnt version: Apple TrueType"); - break; - case 0x74797031: //"typ1" - log.debug("sfnt version: Apple Type 1 housed in sfnt wrapper"); - break; - default: - log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion)); - break; - } - int ntabs = fontFile.readTTFUShort(); - fontFile.skip(6); // 3xTTF_USHORT_SIZE - - dirTabs = new HashMap(); - TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs]; - log.debug("Reading " + ntabs + " dir tables"); - - for (int i = 0; i < ntabs; i++) { - pd[i] = new TTFDirTabEntry(); - String tableName = pd[i].read(fontFile); - dirTabs.put(TTFTableName.getValue(tableName), pd[i]); - } - dirTabs.put(TTFTableName.TABLE_DIRECTORY, - new TTFDirTabEntry(0L, fontFile.getCurrentPos())); - log.debug("dir tables: " + dirTabs.keySet()); - } - - /** - * Read the "head" table, this reads the bounding box and - * sets the upem (unitsPerEM) variable - * @throws IOException in case of an I/O problem - */ - protected void readFontHeader() throws IOException { - seekTab(fontFile, TTFTableName.HEAD, 2 * 4 + 2 * 4); - int flags = fontFile.readTTFUShort(); - if (log.isDebugEnabled()) { - log.debug("flags: " + flags + " - " + Integer.toString(flags, 2)); - } - upem = fontFile.readTTFUShort(); - if (log.isDebugEnabled()) { - log.debug("unit per em: " + upem); - } - - fontFile.skip(16); - - fontBBox1 = fontFile.readTTFShort(); - fontBBox2 = fontFile.readTTFShort(); - fontBBox3 = fontFile.readTTFShort(); - fontBBox4 = fontFile.readTTFShort(); - if (log.isDebugEnabled()) { - log.debug("font bbox: xMin=" + fontBBox1 - + " yMin=" + fontBBox2 - + " xMax=" + fontBBox3 - + " yMax=" + fontBBox4); - } - - fontFile.skip(2 + 2 + 2); - - locaFormat = fontFile.readTTFShort(); - } - - /** - * Read the number of glyphs from the "maxp" table - * @throws IOException in case of an I/O problem - */ - protected void getNumGlyphs() throws IOException { - seekTab(fontFile, TTFTableName.MAXP, 4); - numberOfGlyphs = fontFile.readTTFUShort(); - } - - - /** - * Read the "hhea" table to find the ascender and descender and - * size of "hmtx" table, as a fixed size font might have only - * one width. - * @throws IOException in case of an I/O problem - */ - protected void readHorizontalHeader() - throws IOException { - seekTab(fontFile, TTFTableName.HHEA, 4); - hheaAscender = fontFile.readTTFShort(); - hheaDescender = fontFile.readTTFShort(); - - fontFile.skip(2 + 2 + 3 * 2 + 8 * 2); - nhmtx = fontFile.readTTFUShort(); - - if (log.isDebugEnabled()) { - log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender)); - log.debug("hhea.Descender: " + formatUnitsForDebug(hheaDescender)); - log.debug("Number of horizontal metrics: " + nhmtx); - } - } - - /** - * Read "hmtx" table and put the horizontal metrics - * in the mtxTab array. If the number of metrics is less - * than the number of glyphs (eg fixed size fonts), extend - * the mtxTab array and fill in the missing widths - * @throws IOException in case of an I/O problem - */ - protected void readHorizontalMetrics() - throws IOException { - seekTab(fontFile, TTFTableName.HMTX, 0); - - int mtxSize = Math.max(numberOfGlyphs, nhmtx); - mtxTab = new TTFMtxEntry[mtxSize]; - - if (log.isTraceEnabled()) { - log.trace("*** Widths array: \n"); - } - for (int i = 0; i < mtxSize; i++) { - mtxTab[i] = new TTFMtxEntry(); - } - for (int i = 0; i < nhmtx; i++) { - mtxTab[i].setWx(fontFile.readTTFUShort()); - mtxTab[i].setLsb(fontFile.readTTFUShort()); - - if (log.isTraceEnabled()) { - log.trace(" width[" + i + "] = " - + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";"); - } - } - - if (nhmtx < mtxSize) { - // Fill in the missing widths - int lastWidth = mtxTab[nhmtx - 1].getWx(); - for (int i = nhmtx; i < mtxSize; i++) { - mtxTab[i].setWx(lastWidth); - mtxTab[i].setLsb(fontFile.readTTFUShort()); - } - } - } - - - /** - * Read the "post" table - * containing the PostScript names of the glyphs. - */ - private void readPostScript() throws IOException { - seekTab(fontFile, TTFTableName.POST, 0); - int postFormat = fontFile.readTTFLong(); - italicAngle = fontFile.readTTFULong(); - underlinePosition = fontFile.readTTFShort(); - underlineThickness = fontFile.readTTFShort(); - isFixedPitch = fontFile.readTTFULong(); - - //Skip memory usage values - fontFile.skip(4 * 4); - - log.debug("PostScript format: 0x" + Integer.toHexString(postFormat)); - switch (postFormat) { - case 0x00010000: - log.debug("PostScript format 1"); - postScriptVersion = PostScriptVersion.V1; - for (int i = 0; i < MAC_GLYPH_ORDERING.length; i++) { - mtxTab[i].setName(MAC_GLYPH_ORDERING[i]); - } - break; - case 0x00020000: - log.debug("PostScript format 2"); - postScriptVersion = PostScriptVersion.V2; - int numGlyphStrings = 0; - - // Read Number of Glyphs - int l = fontFile.readTTFUShort(); - - // Read indexes - for (int i = 0; i < l; i++) { - mtxTab[i].setIndex(fontFile.readTTFUShort()); - - if (mtxTab[i].getIndex() > 257) { - //Index is not in the Macintosh standard set - numGlyphStrings++; - } - - if (log.isTraceEnabled()) { - log.trace("PostScript index: " + mtxTab[i].getIndexAsString()); - } - } - - // firstChar=minIndex; - String[] psGlyphsBuffer = new String[numGlyphStrings]; - if (log.isDebugEnabled()) { - log.debug("Reading " + numGlyphStrings - + " glyphnames, that are not in the standard Macintosh" - + " set. Total number of glyphs=" + l); - } - for (int i = 0; i < psGlyphsBuffer.length; i++) { - psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte()); - } - - //Set glyph names - for (int i = 0; i < l; i++) { - if (mtxTab[i].getIndex() < MAC_GLYPH_ORDERING.length) { - mtxTab[i].setName(MAC_GLYPH_ORDERING[mtxTab[i].getIndex()]); - } else { - if (!mtxTab[i].isIndexReserved()) { - int k = mtxTab[i].getIndex() - MAC_GLYPH_ORDERING.length; - - if (log.isTraceEnabled()) { - log.trace(k + " i=" + i + " mtx=" + mtxTab.length - + " ps=" + psGlyphsBuffer.length); - } - - mtxTab[i].setName(psGlyphsBuffer[k]); - } - } - } - - break; - case 0x00030000: - // PostScript format 3 contains no glyph names - log.debug("PostScript format 3"); - postScriptVersion = PostScriptVersion.V3; - break; - default: - log.error("Unknown PostScript format: " + postFormat); - postScriptVersion = PostScriptVersion.UNKNOWN; - } - } - - - /** - * Read the "OS/2" table - */ - private void readOS2() throws IOException { - // Check if font is embeddable - TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2); - if (os2Entry != null) { - seekTab(fontFile, TTFTableName.OS2, 0); - int version = fontFile.readTTFUShort(); - if (log.isDebugEnabled()) { - log.debug("OS/2 table: version=" + version - + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength()); - } - fontFile.skip(2); //xAvgCharWidth - this.usWeightClass = fontFile.readTTFUShort(); - - // usWidthClass - fontFile.skip(2); - - int fsType = fontFile.readTTFUShort(); - if (fsType == 2) { - isEmbeddable = false; - } else { - isEmbeddable = true; - } - fontFile.skip(11 * 2); - fontFile.skip(10); //panose array - fontFile.skip(4 * 4); //unicode ranges - fontFile.skip(4); - fontFile.skip(3 * 2); - int v; - os2Ascender = fontFile.readTTFShort(); //sTypoAscender - os2Descender = fontFile.readTTFShort(); //sTypoDescender - if (log.isDebugEnabled()) { - log.debug("sTypoAscender: " + os2Ascender - + " -> internal " + convertTTFUnit2PDFUnit(os2Ascender)); - log.debug("sTypoDescender: " + os2Descender - + " -> internal " + convertTTFUnit2PDFUnit(os2Descender)); - } - v = fontFile.readTTFShort(); //sTypoLineGap - if (log.isDebugEnabled()) { - log.debug("sTypoLineGap: " + v); - } - v = fontFile.readTTFUShort(); //usWinAscent - if (log.isDebugEnabled()) { - log.debug("usWinAscent: " + formatUnitsForDebug(v)); - } - v = fontFile.readTTFUShort(); //usWinDescent - if (log.isDebugEnabled()) { - log.debug("usWinDescent: " + formatUnitsForDebug(v)); - } - - //version 1 OS/2 table might end here - if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) { - fontFile.skip(2 * 4); - this.os2xHeight = fontFile.readTTFShort(); //sxHeight - this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight - if (log.isDebugEnabled()) { - log.debug("sxHeight: " + this.os2xHeight); - log.debug("sCapHeight: " + this.os2CapHeight); - } - } - - } else { - isEmbeddable = true; - } - } - - /** - * Read the "loca" table. - * @throws IOException In case of a I/O problem - */ - protected final void readIndexToLocation() - throws IOException { - if (!seekTab(fontFile, TTFTableName.LOCA, 0)) { - throw new IOException("'loca' table not found, happens when the font file doesn't" - + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)"); - } - for (int i = 0; i < numberOfGlyphs; i++) { - mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong() - : (fontFile.readTTFUShort() << 1)); - } - lastLoca = (locaFormat == 1 ? fontFile.readTTFULong() - : (fontFile.readTTFUShort() << 1)); +/** + * Reads a TrueType file or a TrueType Collection. + * The TrueType spec can be found at the Microsoft. + * Typography site: http://www.microsoft.com/truetype/ + */ +public class TTFFile extends OpenFont { + + public TTFFile() { + this(true, false); } /** - * Read the "glyf" table to find the bounding boxes. - * @throws IOException In case of a I/O problem + * Constructor + * @param useKerning true if kerning data should be loaded + * @param useAdvanced true if advanced typographic tables should be loaded */ - private void readGlyf() throws IOException { - TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.GLYF); - if (dirTab == null) { - throw new IOException("glyf table not found, cannot continue"); - } - for (int i = 0; i < (numberOfGlyphs - 1); i++) { - if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) { - fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset()); - fontFile.skip(2); - final int[] bbox = { - fontFile.readTTFShort(), - fontFile.readTTFShort(), - fontFile.readTTFShort(), - fontFile.readTTFShort()}; - mtxTab[i].setBoundingBox(bbox); - } else { - mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox()); - } - } - - - long n = (dirTabs.get(TTFTableName.GLYF)).getOffset(); - for (int i = 0; i < numberOfGlyphs; i++) { - if ((i + 1) >= mtxTab.length - || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) { - fontFile.seekSet(n + mtxTab[i].getOffset()); - fontFile.skip(2); - final int[] bbox = { - fontFile.readTTFShort(), - fontFile.readTTFShort(), - fontFile.readTTFShort(), - fontFile.readTTFShort()}; - mtxTab[i].setBoundingBox(bbox); - } else { - /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/ - final int bbox0 = mtxTab[0].getBoundingBox()[0]; - final int[] bbox = {bbox0, bbox0, bbox0, bbox0}; - mtxTab[i].setBoundingBox(bbox); - /* Original code - mtxTab[i].bbox[0] = mtxTab[0].bbox[0]; - mtxTab[i].bbox[1] = mtxTab[0].bbox[0]; - mtxTab[i].bbox[2] = mtxTab[0].bbox[0]; - mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */ - } - if (log.isTraceEnabled()) { - log.trace(mtxTab[i].toString(this)); - } - } + public TTFFile(boolean useKerning, boolean useAdvanced) { + super(useKerning, useAdvanced); } /** * Read the "name" table. * @throws IOException In case of a I/O problem */ - private void readName() throws IOException { - seekTab(fontFile, TTFTableName.NAME, 2); + protected void readName() throws IOException { + seekTab(fontFile, OFTableName.NAME, 2); int i = fontFile.getCurrentPos(); int n = fontFile.readTTFUShort(); int j = fontFile.readTTFUShort() + i - 2; @@ -1487,572 +112,84 @@ public class TTFFile { } /** - * Read the "PCLT" table to find xHeight and capHeight. + * Read the "glyf" table to find the bounding boxes. * @throws IOException In case of a I/O problem */ - private boolean readPCLT() throws IOException { - TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT); - if (dirTab != null) { - fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2); - xHeight = fontFile.readTTFUShort(); - log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight)); - fontFile.skip(2 * 2); - capHeight = fontFile.readTTFUShort(); - log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight)); - fontFile.skip(2 + 16 + 8 + 6 + 1 + 1); - - int serifStyle = fontFile.readTTFUByte(); - serifStyle = serifStyle >> 6; - serifStyle = serifStyle & 3; - if (serifStyle == 1) { - hasSerifs = false; - } else { - hasSerifs = true; - } - return true; - } else { - return false; - } - } - - /** - * Determines the right source for the ascender and descender values. The problem here is - * that the interpretation of these values is not the same for every font. There doesn't seem - * to be a uniform definition of an ascender and a descender. In some fonts - * the hhea values are defined after the Apple interpretation, but not in every font. The - * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the - * baseline so we need values which add up more or less to the "em box". However, due to - * accent modifiers a character can grow beyond the em box. - */ - private void determineAscDesc() { - int hheaBoxHeight = hheaAscender - hheaDescender; - int os2BoxHeight = os2Ascender - os2Descender; - if (os2Ascender > 0 && os2BoxHeight <= upem) { - ascender = os2Ascender; - descender = os2Descender; - } else if (hheaAscender > 0 && hheaBoxHeight <= upem) { - ascender = hheaAscender; - descender = hheaDescender; - } else { - if (os2Ascender > 0) { - //Fall back to info from OS/2 if possible - ascender = os2Ascender; - descender = os2Descender; - } else { - ascender = hheaAscender; - descender = hheaDescender; - } - } - - if (log.isDebugEnabled()) { - log.debug("Font box height: " + (ascender - descender)); - if (ascender - descender > upem) { - log.debug("Ascender and descender together are larger than the em box."); - } + private void readGlyf() throws IOException { + OFDirTabEntry dirTab = dirTabs.get(OFTableName.GLYF); + if (dirTab == null) { + throw new IOException("glyf table not found, cannot continue"); } - } - - private void guessVerticalMetricsFromGlyphBBox() { - // Approximate capHeight from height of "H" - // It's most unlikely that a font misses the PCLT table - // This also assumes that postscriptnames exists ("H") - // Should look it up in the cmap (that wouldn't help - // for charsets without H anyway...) - // Same for xHeight with the letter "x" - int localCapHeight = 0; - int localXHeight = 0; - int localAscender = 0; - int localDescender = 0; - for (int i = 0; i < mtxTab.length; i++) { - if ("H".equals(mtxTab[i].getName())) { - localCapHeight = mtxTab[i].getBoundingBox()[3]; - } else if ("x".equals(mtxTab[i].getName())) { - localXHeight = mtxTab[i].getBoundingBox()[3]; - } else if ("d".equals(mtxTab[i].getName())) { - localAscender = mtxTab[i].getBoundingBox()[3]; - } else if ("p".equals(mtxTab[i].getName())) { - localDescender = mtxTab[i].getBoundingBox()[1]; + for (int i = 0; i < (numberOfGlyphs - 1); i++) { + if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) { + fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset()); + fontFile.skip(2); + final int[] bbox = { + fontFile.readTTFShort(), + fontFile.readTTFShort(), + fontFile.readTTFShort(), + fontFile.readTTFShort()}; + mtxTab[i].setBoundingBox(bbox); } else { - // OpenType Fonts with a version 3.0 "post" table don't have glyph names. - // Use Unicode indices instead. - List unicodeIndex = mtxTab[i].getUnicodeIndex(); - if (unicodeIndex.size() > 0) { - //Only the first index is used - char ch = (char)((Integer)unicodeIndex.get(0)).intValue(); - if (ch == 'H') { - localCapHeight = mtxTab[i].getBoundingBox()[3]; - } else if (ch == 'x') { - localXHeight = mtxTab[i].getBoundingBox()[3]; - } else if (ch == 'd') { - localAscender = mtxTab[i].getBoundingBox()[3]; - } else if (ch == 'p') { - localDescender = mtxTab[i].getBoundingBox()[1]; - } - } - } - } - if (log.isDebugEnabled()) { - log.debug("Ascender from glyph 'd': " + formatUnitsForDebug(localAscender)); - log.debug("Descender from glyph 'p': " + formatUnitsForDebug(localDescender)); - } - if (ascender - descender > upem) { - log.debug("Replacing specified ascender/descender with derived values to get values" - + " which fit in the em box."); - ascender = localAscender; - descender = localDescender; - } - - if (log.isDebugEnabled()) { - log.debug("xHeight from glyph 'x': " + formatUnitsForDebug(localXHeight)); - log.debug("CapHeight from glyph 'H': " + formatUnitsForDebug(localCapHeight)); - } - if (capHeight == 0) { - capHeight = localCapHeight; - if (capHeight == 0) { - capHeight = os2CapHeight; - } - if (capHeight == 0) { - log.debug("capHeight value could not be determined." - + " The font may not work as expected."); - } - } - if (xHeight == 0) { - xHeight = localXHeight; - if (xHeight == 0) { - xHeight = os2xHeight; - } - if (xHeight == 0) { - log.debug("xHeight value could not be determined." - + " The font may not work as expected."); - } - } - } - - /** - * Read the kerning table, create a table for both CIDs and - * winAnsiEncoding. - * @throws IOException In case of a I/O problem - */ - private void readKerning() throws IOException { - // Read kerning - kerningTab = new HashMap>(); - ansiKerningTab = new HashMap>(); - TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN); - if (dirTab != null) { - seekTab(fontFile, TTFTableName.KERN, 2); - for (int n = fontFile.readTTFUShort(); n > 0; n--) { - fontFile.skip(2 * 2); - int k = fontFile.readTTFUShort(); - if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) { - return; - } - if ((k >> 8) != 0) { - continue; - } - - k = fontFile.readTTFUShort(); - fontFile.skip(3 * 2); - while (k-- > 0) { - int i = fontFile.readTTFUShort(); - int j = fontFile.readTTFUShort(); - int kpx = fontFile.readTTFShort(); - if (kpx != 0) { - // CID kerning table entry, using unicode indexes - final Integer iObj = glyphToUnicode(i); - final Integer u2 = glyphToUnicode(j); - if (iObj == null) { - // happens for many fonts (Ubuntu font set), - // stray entries in the kerning table?? - log.debug("Ignoring kerning pair because no Unicode index was" - + " found for the first glyph " + i); - } else if (u2 == null) { - log.debug("Ignoring kerning pair because Unicode index was" - + " found for the second glyph " + i); - } else { - Map adjTab = kerningTab.get(iObj); - if (adjTab == null) { - adjTab = new HashMap(); - } - adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx))); - kerningTab.put(iObj, adjTab); - } - } - } - } - - // Create winAnsiEncoded kerning table from kerningTab - // (could probably be simplified, for now we remap back to CID indexes and - // then to winAnsi) - for (Integer unicodeKey1 : kerningTab.keySet()) { - Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue()); - Map akpx = new HashMap(); - Map ckpx = kerningTab.get(unicodeKey1); - - for (Integer unicodeKey2 : ckpx.keySet()) { - Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue()); - Integer kern = ckpx.get(unicodeKey2); - - Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator(); - while (uniMap.hasNext()) { - Integer unicodeKey = (Integer)uniMap.next(); - Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); - for (int u = 0; u < ansiKeys.length; u++) { - akpx.put(ansiKeys[u], kern); - } - } - } - - if (akpx.size() > 0) { - Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator(); - while (uniMap.hasNext()) { - Integer unicodeKey = (Integer)uniMap.next(); - Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); - for (int u = 0; u < ansiKeys.length; u++) { - ansiKerningTab.put(ansiKeys[u], akpx); - } - } - } + mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox()); } } - } - /** - * Streams a font. - * @param ttfOut The interface for streaming TrueType tables. - * @exception IOException file write error - */ - public void stream(TTFOutputStream ttfOut) throws IOException { - SortedSet> sortedDirTabs = sortDirTabMap(dirTabs); - byte[] file = fontFile.getAllBytes(); - TTFTableOutputStream tableOut = ttfOut.getTableOutputStream(); - TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream(); - ttfOut.startFontStream(); - for (Map.Entry entry : sortedDirTabs) { - int offset = (int) entry.getValue().getOffset(); - int paddedLength = (int) entry.getValue().getLength(); - paddedLength += getPadSize(offset + paddedLength); - if (entry.getKey().equals(TTFTableName.GLYF)) { - streamGlyf(glyphOut, file, offset, paddedLength); + long n = (dirTabs.get(OFTableName.GLYF)).getOffset(); + for (int i = 0; i < numberOfGlyphs; i++) { + if ((i + 1) >= mtxTab.length + || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) { + fontFile.seekSet(n + mtxTab[i].getOffset()); + fontFile.skip(2); + final int[] bbox = { + fontFile.readTTFShort(), + fontFile.readTTFShort(), + fontFile.readTTFShort(), + fontFile.readTTFShort()}; + mtxTab[i].setBoundingBox(bbox); } else { - tableOut.streamTable(file, offset, paddedLength); - } - } - ttfOut.endFontStream(); - } - - private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset, - int tableLength) throws IOException { - //Stream all but the last glyph - int glyphStart = 0; - int glyphEnd = 0; - glyphOut.startGlyphStream(); - for (int i = 0; i < mtxTab.length - 1; i++) { - glyphStart = (int) mtxTab[i].getOffset() + tableOffset; - glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset; - glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart); - } - glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd); - glyphOut.endGlyphStream(); - } - - /** - * Returns the order in which the tables in a TrueType font should be written to file. - * @param directoryTabs the map that is to be sorted. - * @return TTFTablesNames[] an array of table names sorted in the order they should appear in - * the TTF file. - */ - SortedSet> - sortDirTabMap(Map directoryTabs) { - SortedSet> sortedSet - = new TreeSet>( - new Comparator>() { - - public int compare(Entry o1, - Entry o2) { - return (int) (o1.getValue().getOffset() - o2.getValue().getOffset()); - } - }); - sortedSet.addAll(directoryTabs.entrySet()); - return sortedSet; - } - - /** - * Returns this font's character to glyph mapping. - * - * @return the font's cmap - */ - public List getCMaps() { - return cmaps; - } - - /** - * Check if this is a TrueType collection and that the given - * name exists in the collection. - * If it does, set offset in fontfile to the beginning of - * the Table Directory for that font. - * @param name The name to check - * @return True if not collection or font name present, false otherwise - * @throws IOException In case of an I/O problem - */ - protected final boolean checkTTC(String name) throws IOException { - String tag = fontFile.readTTFString(4); - - if ("ttcf".equals(tag)) { - // This is a TrueType Collection - fontFile.skip(4); - - // Read directory offsets - int numDirectories = (int)fontFile.readTTFULong(); - // int numDirectories=in.readTTFUShort(); - long[] dirOffsets = new long[numDirectories]; - for (int i = 0; i < numDirectories; i++) { - dirOffsets[i] = fontFile.readTTFULong(); - } - - log.info("This is a TrueType collection file with " - + numDirectories + " fonts"); - log.info("Containing the following fonts: "); - // Read all the directories and name tables to check - // If the font exists - this is a bit ugly, but... - boolean found = false; - - // Iterate through all name tables even if font - // Is found, just to show all the names - long dirTabOffset = 0; - for (int i = 0; (i < numDirectories); i++) { - fontFile.seekSet(dirOffsets[i]); - readDirTabs(); - - readName(); - - if (fullName.equals(name)) { - found = true; - dirTabOffset = dirOffsets[i]; - log.info(fullName + " <-- selected"); - } else { - log.info(fullName); - } - - // Reset names - notice = ""; - fullName = ""; - familyNames.clear(); - postScriptName = ""; - subFamilyName = ""; - } - - fontFile.seekSet(dirTabOffset); - return found; - } else { - fontFile.seekSet(0); - return true; - } - } - - /** - * Return TTC font names - * @param in FontFileReader to read from - * @return True if not collection or font name present, false otherwise - * @throws IOException In case of an I/O problem - */ - public final List getTTCnames(FontFileReader in) throws IOException { - this.fontFile = in; - - List fontNames = new ArrayList(); - String tag = in.readTTFString(4); - - if ("ttcf".equals(tag)) { - // This is a TrueType Collection - in.skip(4); - - // Read directory offsets - int numDirectories = (int)in.readTTFULong(); - long[] dirOffsets = new long[numDirectories]; - for (int i = 0; i < numDirectories; i++) { - dirOffsets[i] = in.readTTFULong(); - } - - log.info("This is a TrueType collection file with " - + numDirectories + " fonts"); - log.info("Containing the following fonts: "); - - for (int i = 0; (i < numDirectories); i++) { - in.seekSet(dirOffsets[i]); - readDirTabs(); - - readName(); - - log.info(fullName); - fontNames.add(fullName); - - // Reset names - notice = ""; - fullName = ""; - familyNames.clear(); - postScriptName = ""; - subFamilyName = ""; + /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/ + final int bbox0 = mtxTab[0].getBoundingBox()[0]; + final int[] bbox = {bbox0, bbox0, bbox0, bbox0}; + mtxTab[i].setBoundingBox(bbox); + /* Original code + mtxTab[i].bbox[0] = mtxTab[0].bbox[0]; + mtxTab[i].bbox[1] = mtxTab[0].bbox[0]; + mtxTab[i].bbox[2] = mtxTab[0].bbox[0]; + mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */ } - - in.seekSet(0); - return fontNames; - } else { - log.error("Not a TTC!"); - return null; - } - } - - /* - * Helper classes, they are not very efficient, but that really - * doesn't matter... - */ - private Integer[] unicodeToWinAnsi(int unicode) { - List ret = new ArrayList(); - for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) { - if (unicode == Glyphs.WINANSI_ENCODING[i]) { - ret.add(new Integer(i)); + if (log.isTraceEnabled()) { + log.trace(mtxTab[i].toString(this)); } } - return ret.toArray(new Integer[0]); - } - - /** - * Dumps a few informational values to System.out. - */ - public void printStuff() { - System.out.println("Font name: " + postScriptName); - System.out.println("Full name: " + fullName); - System.out.println("Family name: " + familyNames); - System.out.println("Subfamily name: " + subFamilyName); - System.out.println("Notice: " + notice); - System.out.println("xHeight: " + convertTTFUnit2PDFUnit(xHeight)); - System.out.println("capheight: " + convertTTFUnit2PDFUnit(capHeight)); - - int italic = (int)(italicAngle >> 16); - System.out.println("Italic: " + italic); - System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000)); - if ((italicAngle % 0x10000) > 0) { - System.out.print("." - + (short)((italicAngle % 0x10000) * 1000) - / 0x10000); - } - System.out.println(); - System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender)); - System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender)); - System.out.println("FontBBox: [" + convertTTFUnit2PDFUnit(fontBBox1) - + " " + convertTTFUnit2PDFUnit(fontBBox2) + " " - + convertTTFUnit2PDFUnit(fontBBox3) + " " - + convertTTFUnit2PDFUnit(fontBBox4) + "]"); - } - - private String formatUnitsForDebug(int units) { - return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units"; - } - - /** - * Map a glyph index to the corresponding unicode code point - * - * @param glyphIndex - * @return unicode code point - */ - private Integer glyphToUnicode(int glyphIndex) { - return glyphToUnicodeMap.get(new Integer(glyphIndex)); - } - - /** - * Map a unicode code point to the corresponding glyph index - * - * @param unicodeIndex unicode code point - * @return glyph index - */ - private Integer unicodeToGlyph(int unicodeIndex) throws IOException { - final Integer result - = unicodeToGlyphMap.get(new Integer(unicodeIndex)); - if (result == null) { - throw new IOException( - "Glyph index not found for unicode value " + unicodeIndex); - } - return result; - } - - String getGlyphName(int glyphIndex) { - return mtxTab[glyphIndex].getName(); - } - - /** - * Determine if advanced (typographic) table is present. - * @return true if advanced (typographic) table is present - */ - public boolean hasAdvancedTable() { - if (advancedTableReader != null) { - return advancedTableReader.hasAdvancedTable(); - } else { - return false; - } } - /** - * Returns the GDEF table or null if none present. - * @return the GDEF table - */ - public GlyphDefinitionTable getGDEF() { - if (advancedTableReader != null) { - return advancedTableReader.getGDEF(); - } else { - return null; - } + @Override + protected void updateBBoxAndOffset() throws IOException { + readIndexToLocation(); + readGlyf(); } /** - * Returns the GSUB table or null if none present. - * @return the GSUB table + * Read the "loca" table. + * @throws IOException In case of a I/O problem */ - public GlyphSubstitutionTable getGSUB() { - if (advancedTableReader != null) { - return advancedTableReader.getGSUB(); - } else { - return null; + protected final void readIndexToLocation() + throws IOException { + if (!seekTab(fontFile, OFTableName.LOCA, 0)) { + throw new IOException("'loca' table not found, happens when the font file doesn't" + + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)"); } - } - - /** - * Returns the GPOS table or null if none present. - * @return the GPOS table - */ - public GlyphPositioningTable getGPOS() { - if (advancedTableReader != null) { - return advancedTableReader.getGPOS(); - } else { - return null; + for (int i = 0; i < numberOfGlyphs; i++) { + mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong() + : (fontFile.readTTFUShort() << 1)); } + lastLoca = (locaFormat == 1 ? fontFile.readTTFULong() + : (fontFile.readTTFUShort() << 1)); } - /** - * Static main method to get info about a TrueType font. - * @param args The command line arguments - */ - public static void main(String[] args) { - InputStream stream = null; - try { - boolean useKerning = true; - boolean useAdvanced = true; - TTFFile ttfFile = new TTFFile(useKerning, useAdvanced); - - stream = new FileInputStream(args[0]); - FontFileReader reader = new FontFileReader(stream); - - String name = null; - if (args.length >= 2) { - name = args[1]; - } - - ttfFile.readFont(reader, name); - ttfFile.printStuff(); - - } catch (IOException ioe) { - System.err.println("Problem reading font: " + ioe.toString()); - ioe.printStackTrace(System.err); - } finally { - IOUtils.closeQuietly(stream); - } + @Override + protected void initializeFont(FontFileReader in) throws IOException { + fontFile = in; } } diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java deleted file mode 100644 index b7adbd4c9..000000000 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ /dev/null @@ -1,252 +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.fonts.truetype; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.io.IOUtils; - -import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.fonts.CIDFontType; -import org.apache.fop.fonts.CMapSegment; -import org.apache.fop.fonts.EmbeddingMode; -import org.apache.fop.fonts.EncodingMode; -import org.apache.fop.fonts.FontLoader; -import org.apache.fop.fonts.FontType; -import org.apache.fop.fonts.MultiByteFont; -import org.apache.fop.fonts.NamedCharacter; -import org.apache.fop.fonts.SingleByteFont; -import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; -import org.apache.fop.util.HexEncoder; - -/** - * Loads a TrueType font into memory directly from the original font file. - */ -public class TTFFontLoader extends FontLoader { - - private MultiByteFont multiFont; - private SingleByteFont singleFont; - private final String subFontName; - private EncodingMode encodingMode; - private EmbeddingMode embeddingMode; - - /** - * Default constructor - * @param fontFileURI the URI representing the font file - * @param resourceResolver the resource resolver for font URI resolution - */ - public TTFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) { - this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver); - } - - /** - * Additional constructor for TrueType Collections. - * @param fontFileURI the URI representing the font file - * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal - * TrueType fonts) - * @param embedded indicates whether the font is embedded or referenced - * @param embeddingMode the embedding mode of the font - * @param encodingMode the requested encoding mode - * @param useKerning true to enable loading kerning info if available, false to disable - * @param useAdvanced true to enable loading advanced info if available, false to disable - * @param resolver the FontResolver for font URI resolution - */ - public TTFFontLoader(URI fontFileURI, String subFontName, boolean embedded, - EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning, - boolean useAdvanced, InternalResourceResolver resolver) { - super(fontFileURI, embedded, useKerning, useAdvanced, resolver); - this.subFontName = subFontName; - this.encodingMode = encodingMode; - this.embeddingMode = embeddingMode; - if (this.encodingMode == EncodingMode.AUTO) { - this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType - } - if (this.embeddingMode == EmbeddingMode.AUTO) { - this.embeddingMode = EmbeddingMode.SUBSET; - } - } - - /** {@inheritDoc} */ - protected void read() throws IOException { - read(this.subFontName); - } - - /** - * Reads a TrueType font. - * @param ttcFontName the TrueType sub-font name of TrueType Collection (may be null for - * normal TrueType fonts) - * @throws IOException if an I/O error occurs - */ - private void read(String ttcFontName) throws IOException { - InputStream in = resourceResolver.getResource(this.fontFileURI); - try { - TTFFile ttf = new TTFFile(useKerning, useAdvanced); - FontFileReader reader = new FontFileReader(in); - boolean supported = ttf.readFont(reader, ttcFontName); - if (!supported) { - throw new IOException("TrueType font is not supported: " + fontFileURI); - } - buildFont(ttf, ttcFontName); - loaded = true; - } finally { - IOUtils.closeQuietly(in); - } - } - - - private void buildFont(TTFFile ttf, String ttcFontName) { - if (ttf.isCFF()) { - throw new UnsupportedOperationException( - "OpenType fonts with CFF data are not supported, yet"); - } - - boolean isCid = this.embedded; - if (this.encodingMode == EncodingMode.SINGLE_BYTE) { - isCid = false; - } - - if (isCid) { - multiFont = new MultiByteFont(resourceResolver, embeddingMode); - returnFont = multiFont; - multiFont.setTTCName(ttcFontName); - } else { - singleFont = new SingleByteFont(resourceResolver); - returnFont = singleFont; - } - - returnFont.setFontName(ttf.getPostScriptName()); - returnFont.setFullName(ttf.getFullName()); - returnFont.setFamilyNames(ttf.getFamilyNames()); - returnFont.setFontSubFamilyName(ttf.getSubFamilyName()); - returnFont.setCapHeight(ttf.getCapHeight()); - returnFont.setXHeight(ttf.getXHeight()); - returnFont.setAscender(ttf.getLowerCaseAscent()); - returnFont.setDescender(ttf.getLowerCaseDescent()); - returnFont.setFontBBox(ttf.getFontBBox()); - returnFont.setFlags(ttf.getFlags()); - returnFont.setStemV(Integer.parseInt(ttf.getStemV())); //not used for TTF - returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle())); - returnFont.setMissingWidth(0); - returnFont.setWeight(ttf.getWeightClass()); - returnFont.setEmbeddingMode(this.embeddingMode); - if (isCid) { - multiFont.setCIDType(CIDFontType.CIDTYPE2); - int[] wx = ttf.getWidths(); - multiFont.setWidthArray(wx); - } else { - singleFont.setFontType(FontType.TRUETYPE); - singleFont.setEncoding(ttf.getCharSetName()); - returnFont.setFirstChar(ttf.getFirstChar()); - returnFont.setLastChar(ttf.getLastChar()); - singleFont.setTrueTypePostScriptVersion(ttf.getPostScriptVersion()); - copyWidthsSingleByte(ttf); - } - returnFont.setCMap(getCMap(ttf)); - - if (useKerning) { - copyKerning(ttf, isCid); - } - if (useAdvanced) { - copyAdvanced(ttf); - } - if (this.embedded) { - if (ttf.isEmbeddable()) { - returnFont.setEmbedURI(this.fontFileURI); - } else { - String msg = "The font " + this.fontFileURI + " is not embeddable due to a" - + " licensing restriction."; - throw new RuntimeException(msg); - } - } - } - - private CMapSegment[] getCMap(TTFFile ttf) { - CMapSegment[] array = new CMapSegment[ttf.getCMaps().size()]; - return ttf.getCMaps().toArray(array); - } - - private void copyWidthsSingleByte(TTFFile ttf) { - int[] wx = ttf.getWidths(); - for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { - singleFont.setWidth(i, ttf.getCharWidth(i)); - } - - for (CMapSegment segment : ttf.getCMaps()) { - if (segment.getUnicodeStart() < 0xFFFE) { - for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) { - int codePoint = singleFont.getEncoding().mapChar(u); - if (codePoint <= 0) { - int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart(); - String glyphName = ttf.getGlyphName(glyphIndex); - if (glyphName.length() == 0 && ttf.getPostScriptVersion() != PostScriptVersion.V2) { - glyphName = "u" + HexEncoder.encode(u); - } - if (glyphName.length() > 0) { - String unicode = Character.toString(u); - NamedCharacter nc = new NamedCharacter(glyphName, unicode); - singleFont.addUnencodedCharacter(nc, wx[glyphIndex]); - } - } - } - } - } - } - - /** - * Copy kerning information. - */ - private void copyKerning(TTFFile ttf, boolean isCid) { - - // Get kerning - Set kerningSet; - if (isCid) { - kerningSet = ttf.getKerning().keySet(); - } else { - kerningSet = ttf.getAnsiKerning().keySet(); - } - - for (Integer kpx1 : kerningSet) { - Map h2; - if (isCid) { - h2 = ttf.getKerning().get(kpx1); - } else { - h2 = ttf.getAnsiKerning().get(kpx1); - } - returnFont.putKerningEntry(kpx1, h2); - } - } - - /** - * Copy advanced typographic information. - */ - private void copyAdvanced(TTFFile ttf) { - if (returnFont instanceof MultiByteFont) { - MultiByteFont mbf = (MultiByteFont) returnFont; - mbf.setGDEF(ttf.getGDEF()); - mbf.setGSUB(ttf.getGSUB()); - mbf.setGPOS(ttf.getGPOS()); - } - } - -} diff --git a/src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java deleted file mode 100644 index 6884a633d..000000000 --- a/src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java +++ /dev/null @@ -1,195 +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.fonts.truetype; - -import java.util.List; - -/** - * This class represents a TrueType Mtx Entry. - */ -class TTFMtxEntry { - - private int wx; - private int lsb; - private String name = ""; - private int index; - private List unicodeIndex = new java.util.ArrayList(); - private int[] boundingBox = new int[4]; - private long offset; - private byte found = 0; - - /** - * Returns a String representation of this object. - * - * @param t TTFFile to use for unit conversion - * @return String String representation - */ - public String toString(TTFFile t) { - return "Glyph " + name + " index: " + getIndexAsString() + " bbox [" - + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " " - + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " " - + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " " - + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: " - + t.convertTTFUnit2PDFUnit(wx); - } - - /** - * Returns the boundingBox. - * @return int[] - */ - public int[] getBoundingBox() { - return boundingBox; - } - - /** - * Sets the boundingBox. - * @param boundingBox The boundingBox to set - */ - public void setBoundingBox(int[] boundingBox) { - this.boundingBox = boundingBox; - } - - /** - * Returns the found. - * @return byte - */ - public byte getFound() { - return found; - } - - /** - * Returns the index. - * @return int - */ - public int getIndex() { - return index; - } - - /** - * Determines whether this index represents a reserved character. - * @return True if it is reserved - */ - public boolean isIndexReserved() { - return (getIndex() >= 32768) && (getIndex() <= 65535); - } - - /** - * Returns a String representation of the index taking into account if - * the index is in the reserved range. - * @return index as String - */ - public String getIndexAsString() { - if (isIndexReserved()) { - return Integer.toString(getIndex()) + " (reserved)"; - } else { - return Integer.toString(getIndex()); - } - } - - /** - * Returns the lsb. - * @return int - */ - public int getLsb() { - return lsb; - } - - /** - * Returns the name. - * @return String - */ - public String getName() { - return name; - } - - /** - * Returns the offset. - * @return long - */ - public long getOffset() { - return offset; - } - - /** - * Returns the unicodeIndex. - * @return List - */ - public List getUnicodeIndex() { - return unicodeIndex; - } - - /** - * Returns the wx. - * @return int - */ - public int getWx() { - return wx; - } - - /** - * Sets the found. - * @param found The found to set - */ - public void setFound(byte found) { - this.found = found; - } - - /** - * Sets the index. - * @param index The index to set - */ - public void setIndex(int index) { - this.index = index; - } - - /** - * Sets the lsb. - * @param lsb The lsb to set - */ - public void setLsb(int lsb) { - this.lsb = lsb; - } - - /** - * Sets the name. - * @param name The name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the offset. - * @param offset The offset to set - */ - public void setOffset(long offset) { - this.offset = offset; - } - - /** - * Sets the wx. - * @param wx The wx to set - */ - public void setWx(int wx) { - this.wx = wx; - } - - -} diff --git a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java index c9a0e6c8e..ff46af1c7 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java @@ -43,7 +43,7 @@ public class TTFSubSetFile extends TTFFile { * Offsets in name table to be filled out by table. * The offsets are to the checkSum field */ - private Map offsets = new HashMap(); + private Map offsets = new HashMap(); private int checkSumAdjustmentOffset = 0; private int locaOffset = 0; @@ -67,8 +67,8 @@ public class TTFSubSetFile extends TTFFile { } /** The dir tab entries in the new subset font. */ - private Map newDirTabs - = new HashMap(); + private Map newDirTabs + = new HashMap(); private int determineTableCount() { int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp @@ -117,29 +117,29 @@ public class TTFSubSetFile extends TTFFile { writeUShort((numTables * 16) - searchRange); realSize += 2; // Create space for the table entries (these must be in ASCII alphabetical order[A-Z] then[a-z]) - writeTableName(TTFTableName.OS2); + writeTableName(OFTableName.OS2); if (hasCvt()) { - writeTableName(TTFTableName.CVT); + writeTableName(OFTableName.CVT); } if (hasFpgm()) { - writeTableName(TTFTableName.FPGM); + writeTableName(OFTableName.FPGM); } - writeTableName(TTFTableName.GLYF); - writeTableName(TTFTableName.HEAD); - writeTableName(TTFTableName.HHEA); - writeTableName(TTFTableName.HMTX); - writeTableName(TTFTableName.LOCA); - writeTableName(TTFTableName.MAXP); - writeTableName(TTFTableName.NAME); - writeTableName(TTFTableName.POST); + writeTableName(OFTableName.GLYF); + writeTableName(OFTableName.HEAD); + writeTableName(OFTableName.HHEA); + writeTableName(OFTableName.HMTX); + writeTableName(OFTableName.LOCA); + writeTableName(OFTableName.MAXP); + writeTableName(OFTableName.NAME); + writeTableName(OFTableName.POST); if (hasPrep()) { - writeTableName(TTFTableName.PREP); + writeTableName(OFTableName.PREP); } - newDirTabs.put(TTFTableName.TABLE_DIRECTORY, new TTFDirTabEntry(0, currentPos)); + newDirTabs.put(OFTableName.TABLE_DIRECTORY, new OFDirTabEntry(0, currentPos)); } - private void writeTableName(TTFTableName tableName) { + private void writeTableName(OFTableName tableName) { writeString(tableName.getName()); offsets.put(tableName, currentPos); currentPos += 12; @@ -148,15 +148,15 @@ public class TTFSubSetFile extends TTFFile { private boolean hasCvt() { - return dirTabs.containsKey(TTFTableName.CVT); + return dirTabs.containsKey(OFTableName.CVT); } private boolean hasFpgm() { - return dirTabs.containsKey(TTFTableName.FPGM); + return dirTabs.containsKey(OFTableName.FPGM); } private boolean hasPrep() { - return dirTabs.containsKey(TTFTableName.PREP); + return dirTabs.containsKey(OFTableName.PREP); } /** @@ -165,15 +165,15 @@ public class TTFSubSetFile extends TTFFile { private void createLoca(int size) throws IOException { pad4(); locaOffset = currentPos; - int dirTableOffset = offsets.get(TTFTableName.LOCA); + int dirTableOffset = offsets.get(OFTableName.LOCA); writeULong(dirTableOffset + 4, currentPos); writeULong(dirTableOffset + 8, size * 4 + 4); currentPos += size * 4 + 4; realSize += size * 4 + 4; } - private boolean copyTable(FontFileReader in, TTFTableName tableName) throws IOException { - TTFDirTabEntry entry = dirTabs.get(tableName); + private boolean copyTable(FontFileReader in, OFTableName tableName) throws IOException { + OFDirTabEntry entry = dirTabs.get(tableName); if (entry != null) { pad4(); seekTab(in, tableName, 0); @@ -193,28 +193,28 @@ public class TTFSubSetFile extends TTFFile { * Copy the cvt table as is from original font to subset font */ private boolean createCvt(FontFileReader in) throws IOException { - return copyTable(in, TTFTableName.CVT); + return copyTable(in, OFTableName.CVT); } /** * Copy the fpgm table as is from original font to subset font */ private boolean createFpgm(FontFileReader in) throws IOException { - return copyTable(in, TTFTableName.FPGM); + return copyTable(in, OFTableName.FPGM); } /** * Copy the name table as is from the original. */ private boolean createName(FontFileReader in) throws IOException { - return copyTable(in, TTFTableName.NAME); + return copyTable(in, OFTableName.NAME); } /** * Copy the OS/2 table as is from the original. */ private boolean createOS2(FontFileReader in) throws IOException { - return copyTable(in, TTFTableName.OS2); + return copyTable(in, OFTableName.OS2); } /** @@ -222,8 +222,8 @@ public class TTFSubSetFile extends TTFFile { * and set num glyphs to size */ private void createMaxp(FontFileReader in, int size) throws IOException { - TTFTableName maxp = TTFTableName.MAXP; - TTFDirTabEntry entry = dirTabs.get(maxp); + OFTableName maxp = OFTableName.MAXP; + OFDirTabEntry entry = dirTabs.get(maxp); if (entry != null) { pad4(); seekTab(in, maxp, 0); @@ -240,8 +240,8 @@ public class TTFSubSetFile extends TTFFile { } private void createPost(FontFileReader in) throws IOException { - TTFTableName post = TTFTableName.POST; - TTFDirTabEntry entry = dirTabs.get(post); + OFTableName post = OFTableName.POST; + OFDirTabEntry entry = dirTabs.get(post); if (entry != null) { pad4(); seekTab(in, post, 0); @@ -266,7 +266,7 @@ public class TTFSubSetFile extends TTFFile { * Copy the prep table as is from original font to subset font */ private boolean createPrep(FontFileReader in) throws IOException { - return copyTable(in, TTFTableName.PREP); + return copyTable(in, OFTableName.PREP); } @@ -275,15 +275,15 @@ public class TTFSubSetFile extends TTFFile { * and fill in size of hmtx table */ private void createHhea(FontFileReader in, int size) throws IOException { - TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA); + OFDirTabEntry entry = dirTabs.get(OFTableName.HHEA); if (entry != null) { pad4(); - seekTab(in, TTFTableName.HHEA, 0); + seekTab(in, OFTableName.HHEA, 0); System.arraycopy(in.getBytes((int) entry.getOffset(), (int) entry.getLength()), 0, output, currentPos, (int) entry.getLength()); writeUShort((int) entry.getLength() + currentPos - 2, size); - updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA); + updateCheckSum(currentPos, (int) entry.getLength(), OFTableName.HHEA); currentPos += (int) entry.getLength(); realSize += (int) entry.getLength(); } else { @@ -299,8 +299,8 @@ public class TTFSubSetFile extends TTFFile { * in checkSumAdjustmentOffset */ private void createHead(FontFileReader in) throws IOException { - TTFTableName head = TTFTableName.HEAD; - TTFDirTabEntry entry = dirTabs.get(head); + OFTableName head = OFTableName.HEAD; + OFDirTabEntry entry = dirTabs.get(head); if (entry != null) { pad4(); seekTab(in, head, 0); @@ -329,8 +329,8 @@ public class TTFSubSetFile extends TTFFile { */ private void createGlyf(FontFileReader in, Map glyphs) throws IOException { - TTFTableName glyf = TTFTableName.GLYF; - TTFDirTabEntry entry = dirTabs.get(glyf); + OFTableName glyf = OFTableName.GLYF; + OFDirTabEntry entry = dirTabs.get(glyf); int size = 0; int startPos = 0; int endOffset = 0; // Store this as the last loca @@ -393,10 +393,10 @@ public class TTFSubSetFile extends TTFFile { writeULong(locaOffset + glyphs.size() * 4, endOffset); int locaSize = glyphs.size() * 4 + 4; int checksum = getCheckSum(output, locaOffset, locaSize); - writeULong(offsets.get(TTFTableName.LOCA), checksum); + writeULong(offsets.get(OFTableName.LOCA), checksum); int padSize = (locaOffset + locaSize) % 4; - newDirTabs.put(TTFTableName.LOCA, - new TTFDirTabEntry(locaOffset, locaSize + padSize)); + newDirTabs.put(OFTableName.LOCA, + new OFDirTabEntry(locaOffset, locaSize + padSize)); } else { throw new IOException("Can't find glyf table"); } @@ -420,8 +420,8 @@ public class TTFSubSetFile extends TTFFile { */ private void createHmtx(FontFileReader in, Map glyphs) throws IOException { - TTFTableName hmtx = TTFTableName.HMTX; - TTFDirTabEntry entry = dirTabs.get(hmtx); + OFTableName hmtx = OFTableName.HMTX; + OFDirTabEntry entry = dirTabs.get(hmtx); int longHorMetricSize = glyphs.size() * 2; int leftSideBearingSize = glyphs.size() * 2; @@ -457,11 +457,11 @@ public class TTFSubSetFile extends TTFFile { * new index as (Integer) value) * @throws IOException in case of an I/O problem */ - public void readFont(FontFileReader in, String name, + public void readFont(FontFileReader in, String name, String header, Map glyphs) throws IOException { fontFile = in; //Check if TrueType collection, and that the name exists in the collection - if (!checkTTC(name)) { + if (!checkTTC(header, name)) { throw new IOException("Failed to read font"); } @@ -533,7 +533,7 @@ public class TTFSubSetFile extends TTFFile { glyphOffsets[i + 1] - glyphOffsets[i]); } // Stream the last glyph - TTFDirTabEntry glyf = newDirTabs.get(TTFTableName.GLYF); + OFDirTabEntry glyf = newDirTabs.get(OFTableName.GLYF); long lastGlyphLength = glyf.getLength() - (glyphOffsets[glyphOffsets.length - 1] - glyf.getOffset()); glyphOut.streamGlyph(output, glyphOffsets[glyphOffsets.length - 1], @@ -543,14 +543,14 @@ public class TTFSubSetFile extends TTFFile { @Override public void stream(TTFOutputStream ttfOut) throws IOException { - SortedSet> sortedDirTabs + SortedSet> sortedDirTabs = sortDirTabMap(newDirTabs); TTFTableOutputStream tableOut = ttfOut.getTableOutputStream(); TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream(); ttfOut.startFontStream(); - for (Map.Entry entry : sortedDirTabs) { - if (entry.getKey().equals(TTFTableName.GLYF)) { + for (Map.Entry entry : sortedDirTabs) { + if (entry.getKey().equals(OFTableName.GLYF)) { handleGlyphSubset(glyphOut); } else { tableOut.streamTable(output, (int) entry.getValue().getOffset(), @@ -562,7 +562,7 @@ public class TTFSubSetFile extends TTFFile { private void scanGlyphs(FontFileReader in, Map subsetGlyphs) throws IOException { - TTFDirTabEntry glyfTableInfo = dirTabs.get(TTFTableName.GLYF); + OFDirTabEntry glyfTableInfo = dirTabs.get(OFTableName.GLYF); if (glyfTableInfo == null) { throw new IOException("Glyf table could not be found"); } @@ -663,11 +663,11 @@ public class TTFSubSetFile extends TTFFile { } - private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) { + private void updateCheckSum(int tableStart, int tableSize, OFTableName tableName) { int checksum = getCheckSum(output, tableStart, tableSize); int offset = offsets.get(tableName); int padSize = getPadSize(tableStart + tableSize); - newDirTabs.put(tableName, new TTFDirTabEntry(tableStart, tableSize + padSize)); + newDirTabs.put(tableName, new OFDirTabEntry(tableStart, tableSize + padSize)); writeULong(offset, checksum); writeULong(offset + 4, tableStart); writeULong(offset + 8, tableSize); diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java deleted file mode 100644 index e5ad63128..000000000 --- a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java +++ /dev/null @@ -1,163 +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.fonts.truetype; - - -/** - * Represents table names as found in a TrueType font's Table Directory. - * TrueType fonts may have custom tables so we cannot use an enum. - */ -public final class TTFTableName { - - /** The first table in a TrueType font file containing metadata about other tables. */ - public static final TTFTableName TABLE_DIRECTORY = new TTFTableName("tableDirectory"); - - /** Embedded bitmap data. */ - public static final TTFTableName EBDT = new TTFTableName("EBDT"); - - /** Embedded bitmap location data. */ - public static final TTFTableName EBLC = new TTFTableName("EBLC"); - - /** Embedded bitmap scaling data. */ - public static final TTFTableName EBSC = new TTFTableName("EBSC"); - - /** A FontForge specific table. */ - public static final TTFTableName FFTM = new TTFTableName("FFTM"); - - /** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */ - public static final TTFTableName GDEF = new TTFTableName("GDEF"); - - /** Provides kerning information, mark-to-base, etc. for opentype fonts. */ - public static final TTFTableName GPOS = new TTFTableName("GPOS"); - - /** Provides ligature information, swash, etc. for opentype fonts. */ - public static final TTFTableName GSUB = new TTFTableName("GSUB"); - - /** Linear threshold table. */ - public static final TTFTableName LTSH = new TTFTableName("LTSH"); - - /** OS/2 and Windows specific metrics. */ - public static final TTFTableName OS2 = new TTFTableName("OS/2"); - - /** PCL 5 data. */ - public static final TTFTableName PCLT = new TTFTableName("PCLT"); - - /** Vertical Device Metrics table. */ - public static final TTFTableName VDMX = new TTFTableName("VDMX"); - - /** Character to glyph mapping. */ - public static final TTFTableName CMAP = new TTFTableName("cmap"); - - /** Control Value Table. */ - public static final TTFTableName CVT = new TTFTableName("cvt "); - - /** Font program. */ - public static final TTFTableName FPGM = new TTFTableName("fpgm"); - - /** Grid-fitting and scan conversion procedure (grayscale). */ - public static final TTFTableName GASP = new TTFTableName("gasp"); - - /** Glyph data. */ - public static final TTFTableName GLYF = new TTFTableName("glyf"); - - /** Horizontal device metrics. */ - public static final TTFTableName HDMX = new TTFTableName("hdmx"); - - /** Font header. */ - public static final TTFTableName HEAD = new TTFTableName("head"); - - /** Horizontal header. */ - public static final TTFTableName HHEA = new TTFTableName("hhea"); - - /** Horizontal metrics. */ - public static final TTFTableName HMTX = new TTFTableName("hmtx"); - - /** Kerning. */ - public static final TTFTableName KERN = new TTFTableName("kern"); - - /** Index to location. */ - public static final TTFTableName LOCA = new TTFTableName("loca"); - - /** Maximum profile. */ - public static final TTFTableName MAXP = new TTFTableName("maxp"); - - /** Naming table. */ - public static final TTFTableName NAME = new TTFTableName("name"); - - /** PostScript information. */ - public static final TTFTableName POST = new TTFTableName("post"); - - /** CVT Program. */ - public static final TTFTableName PREP = new TTFTableName("prep"); - - /** Vertical Metrics header. */ - public static final TTFTableName VHEA = new TTFTableName("vhea"); - - /** Vertical Metrics. */ - public static final TTFTableName VMTX = new TTFTableName("vmtx"); - - private final String name; - - private TTFTableName(String name) { - this.name = name; - } - - /** - * Returns the name of the table as it should be in the Directory Table. - */ - public String getName() { - return name; - } - - /** - * Returns an instance of this class corresponding to the given string representation. - * @param tableName table name as in the Table Directory - * @return TTFTableName - */ - public static TTFTableName getValue(String tableName) { - if (tableName != null) { - return new TTFTableName(tableName); - } - throw new IllegalArgumentException("A TrueType font table name must not be null"); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof TTFTableName)) { - return false; - } - TTFTableName to = (TTFTableName) o; - return this.name.equals(to.getName()); - } - - @Override - public String toString() { - return name; - } - -} diff --git a/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java b/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java new file mode 100644 index 000000000..53f0b36b4 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * PDFStream for embeddable OpenType CFF fonts. + */ +public class PDFCFFStreamType0C extends AbstractPDFFontStream { + + private byte[] cffData; + private boolean fullEmbed; + + /** + * Main constructor + * @param fullEmbed Determines whether the font is fully embedded + */ + public PDFCFFStreamType0C(boolean fullEmbed) { + super(); + this.fullEmbed = fullEmbed; + } + + protected int getSizeHint() throws IOException { + if (this.cffData != null) { + return cffData.length; + } else { + return 0; //no hint available + } + } + + /** {@inheritDoc} */ + protected void outputRawStreamData(OutputStream out) throws IOException { + out.write(this.cffData); + } + + /** {@inheritDoc} */ + protected void populateStreamDict(Object lengthEntry) { + String type = (fullEmbed) ? "OpenType" : "CIDFontType0C"; + put("Subtype", new PDFName(type)); + super.populateStreamDict(lengthEntry); + } + + /** + * Sets the CFF font data. + * @param data the font payload + * @param size size of the payload + * @throws IOException in case of an I/O problem + */ + public void setData(byte[] data, int size) throws IOException { + this.cffData = new byte[size]; + System.arraycopy(data, 0, this.cffData, 0, size); + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java index 2fe9f29e0..8fd8d3444 100644 --- a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java +++ b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java @@ -449,14 +449,13 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { private class Rev5Engine extends InitializationEngine { - // private SecureRandom random = new SecureRandom(); - private byte[] userValidationSalt = new byte[8]; - private byte[] userKeySalt = new byte[8]; - private byte[] ownerValidationSalt = new byte[8]; - private byte[] ownerKeySalt = new byte[8]; - private byte[] ueValue; - private byte[] oeValue; - private final boolean encryptMetadata; + protected byte[] userValidationSalt = new byte[8]; + protected byte[] userKeySalt = new byte[8]; + protected byte[] ownerValidationSalt = new byte[8]; + protected byte[] ownerKeySalt = new byte[8]; + protected byte[] ueValue; + protected byte[] oeValue; + protected final boolean encryptMetadata; Rev5Engine(EncryptionSettings encryptionSettings) { super(encryptionSettings); @@ -563,7 +562,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { /** * Algorithm 3.8-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) */ - private void computeUEValue() { + protected void computeUEValue() { digest.reset(); byte[] prepared = preparedUserPassword; byte[] concatenated = new byte[prepared.length + 8]; @@ -577,7 +576,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { /** * Algorithm 3.9-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) */ - private void computeOEValue() { + protected void computeOEValue() { digest.reset(); byte[] prepared = preparedOwnerPassword; byte[] concatenated = new byte[prepared.length + 56]; @@ -615,6 +614,86 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { } } + private class Rev6Engine extends Rev5Engine { + + private MessageDigest digest384; + private MessageDigest digest512; + + Rev6Engine(EncryptionSettings encryptionSettings) { + super(encryptionSettings); + try { + digest384 = MessageDigest.getInstance("SHA-384"); + digest512 = MessageDigest.getInstance("SHA-512"); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e.getMessage()); + } + } + + @Override + protected void computeUValue() { + byte[] userBytes = new byte[16]; + random.nextBytes(userBytes); + System.arraycopy(userBytes, 0, userValidationSalt, 0, 8); + System.arraycopy(userBytes, 8, userKeySalt, 0, 8); + digest.reset(); + byte[] prepared = preparedUserPassword; + byte[] concatenated = new byte[prepared.length + 8]; + System.arraycopy(prepared, 0, concatenated, 0, prepared.length); + System.arraycopy(userValidationSalt, 0, concatenated, prepared.length, 8); + digest.update(concatenated); + byte[] block = digest.digest(); + int blockSize = 32; + byte[] key = new byte[16]; + byte[] iv = new byte[16]; + int length = prepared.length + blockSize; + byte[] data = new byte[length * 64]; + for (int i = 0; i < 64 || i < data[length * 64 - 1] + 32; i++) { + System.arraycopy(block, 0, key, 0, 16); + System.arraycopy(block, 16, iv, 0, 16); + for (int j = 0; j < 64; j++) { + System.arraycopy(prepared, 0, data, j * length, prepared.length); + System.arraycopy(block, 0, data, j * length + prepared.length, blockSize); + } + try { + final Cipher cipher = PDFEncryptionJCE.initCipher(key, false, iv); + data = cipher.doFinal(data); + } catch (IllegalBlockSizeException e) { + throw new IllegalStateException(e.getMessage()); + } catch (BadPaddingException e) { + throw new IllegalStateException(e.getMessage()); + } + int sum = 0; + for (int k = 0; k < 16; k++) { + sum += data[k]; + } + blockSize = 32 + (sum % 3) * 16; + switch (blockSize) { + case 32: + digest.reset(); + digest.update(data); + block = digest.digest(); + break; + case 48: + digest384.reset(); + digest384.update(data); + block = digest384.digest(); + break; + case 64: + digest512.reset(); + digest512.update(data); + block = digest512.digest(); + break; + default: + // not possible + break; + } + length = prepared.length + blockSize; + data = new byte[length * 64]; + } + } + + } + private class EncryptionFilter extends PDFFilter { private int streamNumber; diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 1756f1d56..631499af1 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -56,6 +56,8 @@ import org.apache.fop.fonts.SingleByteEncoding; import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.OFFontLoader; +import org.apache.fop.fonts.truetype.OTFSubSetFile; import org.apache.fop.fonts.truetype.TTFSubSetFile; import org.apache.fop.fonts.type1.PFBData; import org.apache.fop.fonts.type1.PFBParser; @@ -1387,15 +1389,15 @@ public class PDFFactory { int firstChar = singleByteFont.getFirstChar(); int lastChar = singleByteFont.getLastChar(); nonBase14.setWidthMetrics(firstChar, - lastChar, - new PDFArray(null, metrics.getWidths())); + lastChar, + new PDFArray(null, metrics.getWidths())); //Handle encoding SingleByteEncoding mapping = singleByteFont.getEncoding(); if (singleByteFont.isSymbolicFont()) { //no encoding, use the font's encoding if (forceToUnicode) { - generateToUnicodeCmap(nonBase14, mapping); + generateToUnicodeCmap(nonBase14, mapping); } } else if (PDFEncoding.isPredefinedEncoding(mapping.getName())) { font.setEncoding(mapping.getName()); @@ -1403,7 +1405,7 @@ public class PDFFactory { //believed. } else { Object pdfEncoding = createPDFEncoding(mapping, - singleByteFont.getFontName()); + singleByteFont.getFontName()); if (pdfEncoding instanceof PDFEncoding) { font.setEncoding((PDFEncoding)pdfEncoding); } else { @@ -1518,7 +1520,8 @@ public class PDFFactory { // Check if the font is embeddable if (desc.isEmbeddable()) { - AbstractPDFStream stream = makeFontFile(desc); + AbstractPDFStream stream = makeFontFile(desc, fontPrefix); + if (stream != null) { descriptor.setFontFile(desc.getFontType(), stream); getDocument().registerObject(stream); @@ -1564,7 +1567,7 @@ public class PDFFactory { * @param desc FontDescriptor of the font. * @return PDFStream The embedded font file */ - public AbstractPDFStream makeFontFile(FontDescriptor desc) { + public AbstractPDFStream makeFontFile(FontDescriptor desc, String fontPrefix) { if (desc.getFontType() == FontType.OTHER) { throw new IllegalArgumentException("Trying to embed unsupported font type: " + desc.getFontType()); @@ -1578,20 +1581,24 @@ public class PDFFactory { if (in == null) { return null; } else { - AbstractPDFStream embeddedFont; + AbstractPDFStream embeddedFont = null; if (desc.getFontType() == FontType.TYPE0) { MultiByteFont mbfont = (MultiByteFont) font; FontFileReader reader = new FontFileReader(in); byte[] fontBytes; + String header = OFFontLoader.readHeader(reader); + boolean isCFF = mbfont.isOTFFile(); if (font.getEmbeddingMode() == EmbeddingMode.FULL) { fontBytes = reader.getAllBytes(); + if (isCFF) { + //Ensure version 1.6 for full OTF CFF embedding + document.setPDFVersion(Version.V1_6); + } } else { - TTFSubSetFile ttfFile = new TTFSubSetFile(); - ttfFile.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); - fontBytes = ttfFile.getFontSubset(); + fontBytes = getFontSubsetBytes(reader, mbfont, header, fontPrefix, desc, + isCFF); } - embeddedFont = new PDFTTFStream(fontBytes.length); - ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length); + embeddedFont = getFontStream(font, fontBytes, isCFF); } else if (desc.getFontType() == FontType.TYPE1) { PFBParser parser = new PFBParser(); PFBData pfb = parser.parsePFB(in); @@ -1621,6 +1628,32 @@ public class PDFFactory { } } + private byte[] getFontSubsetBytes(FontFileReader reader, MultiByteFont mbfont, String header, + String fontPrefix, FontDescriptor desc, boolean isCFF) throws IOException { + if (isCFF) { + OTFSubSetFile otfFile = new OTFSubSetFile(); + otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), header, mbfont); + return otfFile.getFontSubset(); + } else { + TTFSubSetFile otfFile = new TTFSubSetFile(); + otfFile.readFont(reader, mbfont.getTTCName(), header, mbfont.getUsedGlyphs()); + return otfFile.getFontSubset(); + } + } + + private AbstractPDFStream getFontStream(CustomFont font, byte[] fontBytes, boolean isCFF) + throws IOException { + AbstractPDFStream embeddedFont; + if (isCFF) { + embeddedFont = new PDFCFFStreamType0C(font.getEmbeddingMode() == EmbeddingMode.FULL); + ((PDFCFFStreamType0C) embeddedFont).setData(fontBytes, fontBytes.length); + } else { + embeddedFont = new PDFTTFStream(fontBytes.length); + ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length); + } + return embeddedFont; + } + private CustomFont getCustomFont(FontDescriptor desc) { Typeface tempFont; if (desc instanceof LazyFont) { diff --git a/src/java/org/apache/fop/pdf/PDFFontDescriptor.java b/src/java/org/apache/fop/pdf/PDFFontDescriptor.java index ec4e99101..73dbebc3f 100644 --- a/src/java/org/apache/fop/pdf/PDFFontDescriptor.java +++ b/src/java/org/apache/fop/pdf/PDFFontDescriptor.java @@ -102,6 +102,8 @@ public class PDFFontDescriptor extends PDFDictionary { public void setFontFile(FontType subtype, AbstractPDFStream fontfile) { if (subtype == FontType.TYPE1) { put("FontFile", fontfile); + } else if (fontfile instanceof PDFCFFStreamType0C) { + put("FontFile3", fontfile); } else { put("FontFile2", fontfile); } diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index e57567b88..c22a3ba28 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.cff.CFFStandardString; import org.apache.xmlgraphics.fonts.Glyphs; import org.apache.xmlgraphics.ps.DSCConstants; @@ -50,9 +51,14 @@ import org.apache.fop.fonts.MultiByteFont; import org.apache.fop.fonts.SingleByteEncoding; import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; +import org.apache.fop.fonts.cff.CFFDataReader; +import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry; import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.OFFontLoader; +import org.apache.fop.fonts.truetype.OTFFile; +import org.apache.fop.fonts.truetype.OTFSubSetFile; +import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion; import org.apache.fop.fonts.truetype.TTFFile; -import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; import org.apache.fop.fonts.truetype.TTFOutputStream; import org.apache.fop.fonts.truetype.TTFSubSetFile; import org.apache.fop.render.ps.fonts.PSTTFOutputStream; @@ -221,6 +227,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes, PSEventProducer eventProducer) throws IOException { + boolean embeddedFont = false; FontType fontType = tf.getFontType(); PSFontResource fontResource = null; if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE @@ -232,52 +239,63 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { CustomFont cf = (CustomFont)tf; if (isEmbeddable(cf)) { InputStream in = getInputStreamOnFont(gen, cf); - if (in == null) { + if (in != null) { + if (fontType == FontType.TYPE0) { + if (((MultiByteFont)tf).isOTFFile()) { + checkPostScriptLevel3(gen, eventProducer, "OpenType CFF"); + embedType2CFF(gen, (MultiByteFont) tf, in); + } else { + if (gen.embedIdentityH()) { + checkPostScriptLevel3(gen, eventProducer, "TrueType"); + /* + * First CID-keyed font to be embedded; add + * %%IncludeResource: comment for ProcSet CIDInit. + */ + gen.includeProcsetCIDInitResource(); + } + PSResource cidFontResource; + cidFontResource = embedType2CIDFont(gen, + (MultiByteFont) tf, in); + fontResource = PSFontResource.createFontResource(fontRes, + gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(), + cidFontResource); + } + } + gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes); + if (fontType == FontType.TYPE1) { + embedType1Font(gen, in); + fontResource = PSFontResource.createFontResource(fontRes); + } else if (fontType == FontType.TRUETYPE) { + embedTrueTypeFont(gen, (SingleByteFont) tf, in); + fontResource = PSFontResource.createFontResource(fontRes); + } else { + composeType0Font(gen, (MultiByteFont) tf, in); + } + gen.writeDSCComment(DSCConstants.END_RESOURCE); + gen.getResourceTracker().registerSuppliedResource(fontRes); + embeddedFont = true; + } else { gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName()); log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the" + " PostScript file but could not be embedded!"); - gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); - fontResource = PSFontResource.createFontResource(fontRes); - return fontResource; - } - if (fontType == FontType.TYPE0) { - if (gen.embedIdentityH()) { - checkPostScriptLevel3(gen, eventProducer); - /* - * First CID-keyed font to be embedded; add - * %%IncludeResource: comment for ProcSet CIDInit. - */ - gen.includeProcsetCIDInitResource(); - } - PSResource cidFontResource = embedType2CIDFont(gen, - (MultiByteFont) tf, in); - fontResource = PSFontResource.createFontResource(fontRes, - gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(), - cidFontResource); } - gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes); - if (fontType == FontType.TYPE1) { - embedType1Font(gen, in); - fontResource = PSFontResource.createFontResource(fontRes); - } else if (fontType == FontType.TRUETYPE) { - embedTrueTypeFont(gen, (SingleByteFont) tf, in); - fontResource = PSFontResource.createFontResource(fontRes); - } else { - composeType0Font(gen, (MultiByteFont) tf, in); - } - gen.writeDSCComment(DSCConstants.END_RESOURCE); - gen.getResourceTracker().registerSuppliedResource(fontRes); + } + if (!embeddedFont) { + gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); + fontResource = PSFontResource.createFontResource(fontRes); + return fontResource; } return fontResource; } - private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) { + private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer, + String fontType) { if (gen.getPSLevel() < 3) { if (eventProducer != null) { eventProducer.postscriptLevel3Needed(gen); } else { throw new IllegalStateException("PostScript Level 3 is" - + " required to use TrueType fonts," + + " required to use " + fontType + " fonts," + " configured level is " + gen.getPSLevel()); } @@ -415,6 +433,96 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { gen.writeln("] composefont pop"); } + private static void embedType2CFF(PSGenerator gen, + MultiByteFont font, InputStream fontStream) throws IOException { + FontFileReader reader = new FontFileReader(fontStream); + String header = OFFontLoader.readHeader(reader); + String psName; + CFFDataReader cffReader = new CFFDataReader(reader); + if (cffReader.getFDSelect() != null) { + throw new UnsupportedOperationException("CID-Keyed OTF CFF fonts are not supported" + + " for PostScript output."); + } + + byte[] bytes; + if (font.getEmbeddingMode() == EmbeddingMode.FULL) { + font.setFontName(new String(cffReader.getNameIndex().getValue(0))); + psName = font.getEmbedFontName(); + Map topDICT = cffReader.getTopDictEntries(); + int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue(); + for (int gid = 0; gid < cffReader.getCharStringIndex().getNumObjects(); gid++) { + int sid = cffReader.getSIDFromGID(charsetOffset, gid); + + //Check whether the SID falls into the standard string set + if (sid < 391) { + font.mapUsedGlyphName(gid, + CFFStandardString.getName(sid)); + } else { + int index = sid - 391; + if (index < cffReader.getStringIndex().getNumObjects()) { + font.mapUsedGlyphName(gid, + new String(cffReader.getStringIndex().getValue(index))); + } else { + font.mapUsedGlyphName(gid, ".notdef"); + } + } + } + bytes = OTFFile.getCFFData(reader); + } else { + psName = font.getEmbedFontName(); + OTFSubSetFile otfFile = new OTFSubSetFile(); + otfFile.readFont(reader, psName, header, font); + bytes = otfFile.getFontSubset(); + } + + gen.writeln("%!PS-Adobe-3.0 Resource-FontSet"); + gen.writeln("%%DocumentNeedResources:ProcSet(FontSetInit)"); + gen.writeln("%%Title:(FontSet/" + psName + ")"); + gen.writeln("%%Version: 1.000"); + gen.writeln("%%EndComments"); + gen.writeln("%%IncludeResource:ProcSet(FontSetInit)"); + gen.writeln("%%BeginResource: FontSet (" + psName + ")"); + gen.writeln("/FontSetInit /ProcSet findresource begin"); + //Next line + 1 + String fontDeclaration = "/" + psName + " " + bytes.length + " StartData"; + gen.writeln("%%BeginData: " + (fontDeclaration.length() + 1 + bytes.length) + " Binary Bytes"); + gen.writeln(fontDeclaration); + gen.writeByteArr(bytes); + gen.writeln("%%EndData"); + gen.writeln("%%EndResource"); + + gen.writeln("/" + psName + ".0.enc [ "); + int lengthCount = 0; + int charCount = 1; + int encodingCount = 0; + String line = ""; + for (int gid : font.getUsedGlyphNames().keySet()) { + line += "/" + font.getUsedGlyphNames().get(gid) + " "; + lengthCount++; + charCount++; + if (lengthCount == 8) { + gen.writeln(line); + line = ""; + lengthCount = 0; + } + if (charCount > 256) { + encodingCount++; + charCount = 1; + gen.writeln(line); + line = ""; + lengthCount = 0; + gen.writeln("] def"); + gen.writeln(String.format("/%s.%d %s.%d.enc /%s RE", psName, + encodingCount - 1, psName, encodingCount - 1, psName)); + gen.writeln("/" + psName + "." + encodingCount + ".enc [ "); + } + } + gen.writeln(line); + gen.writeln("] def"); + gen.writeln(String.format("/%s.%d %s.%d.enc /%s RE", psName, encodingCount, + psName, encodingCount, psName)); + } + private static PSResource embedType2CIDFont(PSGenerator gen, MultiByteFont font, InputStream fontStream) throws IOException { assert font.getCIDType() == CIDFontType.CIDTYPE2; @@ -502,17 +610,18 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { } gen.writeln(">] def"); FontFileReader reader = new FontFileReader(fontStream); + String header = OFFontLoader.readHeader(reader); TTFFile ttfFile; if (font.getEmbeddingMode() != EmbeddingMode.FULL) { ttfFile = new TTFSubSetFile(); - ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs()); + //Change the TTFFile to have the abstract method for TTFSubSetFile + ((TTFSubSetFile)ttfFile).readFont(reader, font.getTTCName(), header, font.getUsedGlyphs()); } else { ttfFile = new TTFFile(); ttfFile.readFont(reader, font.getTTCName()); } - createType42DictionaryEntries(gen, font, new CMapSegment[0], ttfFile); gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop"); gen.writeln("end"); @@ -670,7 +779,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { private static PSResource defineDerivedTrueTypeFont(PSGenerator gen, PSEventProducer eventProducer, String baseFontName, String fontName, SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException { - checkPostScriptLevel3(gen, eventProducer); + checkPostScriptLevel3(gen, eventProducer, "TrueType"); PSResource res = new PSResource(PSResource.TYPE_FONT, fontName); gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); gen.commentln("%XGCDependencies: font " + baseFontName); diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index b0b370c79..3d1887f2d 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -40,6 +40,7 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSResource; +import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.LazyFont; @@ -375,7 +376,13 @@ public class PSPainter extends AbstractIFPainter { } Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints); - useFont(fontKey, sizeMillipoints); + PSFontResource res = getDocumentHandler().getPSResourceForFontKey(fontKey); + if (tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile()) { + generator.writeln("/" + res.getName() + ".0 " + + generator.formatDouble(sizeMillipoints / 1000f) + " F"); + } else { + useFont(fontKey, sizeMillipoints); + } if (dp != null && dp[0] != null) { x += dp[0][0]; @@ -408,7 +415,30 @@ public class PSPainter extends AbstractIFPainter { } } } else { - useFont(fontKey, sizeMillipoints); + if (tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile()) { + //Analyze string and split up in order to paint in different sub-fonts/encodings + int curEncoding = 0; + for (int i = start; i < textLen; i++) { + char orgChar = text.charAt(i); + + MultiByteFont mbFont = (MultiByteFont)tf; + int origGlyphIdx = mbFont.findGlyphIndex(orgChar); + int newGlyphIdx = mbFont.getUsedGlyphs().get(origGlyphIdx); + int encoding = newGlyphIdx / 256; + if (encoding != curEncoding) { + if (i != 0) { + writeText(text, start, i - start, letterSpacing, wordSpacing, dp, font, tf, + true); + start = i; + } + generator.writeln("/" + res.getName() + "." + encoding + " " + + generator.formatDouble(sizeMillipoints / 1000f) + " F"); + curEncoding = encoding; + } + } + } else { + useFont(fontKey, sizeMillipoints); + } } writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf, tf instanceof MultiByteFont); @@ -431,12 +461,14 @@ public class PSPainter extends AbstractIFPainter { int lineStart = 0; StringBuffer accText = new StringBuffer(initialSize); StringBuffer sb = new StringBuffer(initialSize); + boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile(); for (int i = start; i < end; i++) { char orgChar = text.charAt(i); char ch; int cw; int xGlyphAdjust = 0; int yGlyphAdjust = 0; + if (CharUtilities.isFixedWidthSpace(orgChar)) { //Fixed width space are rendered as spaces so copy/paste works in a reader ch = font.mapChar(CharUtilities.SPACE); @@ -460,11 +492,16 @@ public class PSPainter extends AbstractIFPainter { xGlyphAdjust -= dp[i + 1][0]; yGlyphAdjust += dp[i + 1][1]; } - if (multiByte) { - accText.append(HexEncoder.encode(ch)); - } else { + if (!multiByte || isOTF) { char codepoint = (char)(ch % 256); - PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text + if (isOTF) { + codepoint -= (((MultiByteFont)tf).getEmbeddingMode() == EmbeddingMode.FULL) ? 0 : 1; + accText.append(HexEncoder.encode(codepoint, 2)); + } else { + PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text + } + } else { + accText.append(HexEncoder.encode(ch)); } if (xGlyphAdjust != 0 || yGlyphAdjust != 0) { needTJ = true; diff --git a/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java b/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java new file mode 100644 index 000000000..64c7642eb --- /dev/null +++ b/test/java/org/apache/fop/fonts/cff/CFFDataReaderTestCase.java @@ -0,0 +1,154 @@ +/* + * 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.cff; + +import java.io.IOException; +import java.util.Map; +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.fontbox.cff.CFFDataInput; + +import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData; +import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry; +import org.apache.fop.fonts.truetype.OTFSubSetFile; + +import static org.junit.Assert.assertEquals; + +public class CFFDataReaderTestCase { + private CFFDataReader cffReader; + + /** + * Initializes the CFFDataReader for testing purposes + */ + @Before + public void setUp() { + cffReader = new CFFDataReader(); + } + + /** + * Parses a test dictionary to verify whether the stored data is read correctly. + * @throws IOException + */ + @Test + public void parseDictData() throws IOException { + byte[] testDictData = prepareDictData(); + Map testTopDict = cffReader.parseDictData(testDictData); + validateDictData(testTopDict); + } + + private byte[] prepareDictData() { + byte[] testDictData = new byte[0]; + //Version + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 392, new int[] { 0 }, -1)); + //Notice + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 393, new int[] { 1 }, -1)); + //Copyright + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 394, new int[] { 12, 0 }, -1)); + //FullName + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 395, new int[] { 2 }, -1)); + //FamilyName + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 396, new int[] { 3 }, -1)); + //Weight + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 397, new int[] { 4 }, -1)); + //isFixedPitch (boolean = false) + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 0, new int[] { 12, 1 }, -1)); + //FontBBox + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + -50, new int[0], -1)); + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + -40, new int[0], -1)); + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 100, new int[0], -1)); + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 120, new int[] { 5 }, -1)); + //charset + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 1234, new int[] { 15 }, -1)); + //CharStrings + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 3654, new int[] { 17 }, -1)); + //Private + testDictData = OTFSubSetFile.concatArray(testDictData, OTFSubSetFile.createNewRef( + 11454, new int[] { 18 }, -1)); + return testDictData; + } + + private void validateDictData(Map dictMap) { + //SID Values (numbers) + assertEquals(dictMap.get("version").getOperands().get(0).intValue(), 392); + assertEquals(dictMap.get("Notice").getOperands().get(0).intValue(), 393); + assertEquals(dictMap.get("Copyright").getOperands().get(0).intValue(), 394); + assertEquals(dictMap.get("FullName").getOperands().get(0).intValue(), 395); + assertEquals(dictMap.get("FamilyName").getOperands().get(0).intValue(), 396); + assertEquals(dictMap.get("Weight").getOperands().get(0).intValue(), 397); + //Boolean comparison + assertEquals(dictMap.get("isFixedPitch").getOperands().get(0).intValue(), 0); + //Array comparison + int[] fontBBox = { -50, -40, 100, 120 }; + DICTEntry fontBBoxEntry = dictMap.get("FontBBox"); + for (int i = 0;i < fontBBoxEntry.getOperands().size();i++) { + assertEquals(fontBBoxEntry.getOperands().get(i).intValue(), fontBBox[i]); + } + //Multi-byte offset (number) + assertEquals(dictMap.get("charset").getOperands().get(0).intValue(), 1234); + assertEquals(dictMap.get("CharStrings").getOperands().get(0).intValue(), 3654); + //Larger offset + assertEquals(dictMap.get("Private").getOperands().get(0).intValue(), 11454); + } + + /** + * Tests the parsing of an example byte data index structure + * @throws IOException + */ + @Test + public void testIndexParsing() throws IOException { + byte[] testIndex = { + 0, 5, //Number of objects + 1, //Offset size + 1, //Offsets... + 5, + 12, + 24, + 27, + 32 + }; + Random randGen = new Random(); + byte[] data = new byte[31]; + for (int i = 0;i < data.length;i++) { + data[i] = (byte)randGen.nextInt(255); + } + testIndex = OTFSubSetFile.concatArray(testIndex, data); + CFFIndexData indexData = cffReader.readIndex(new CFFDataInput(testIndex)); + assertEquals(indexData.getNumObjects(), 5); + assertEquals(indexData.getOffSize(), 1); + assertEquals(indexData.getOffsets().length, 6); + assertEquals(indexData.getOffsets()[5], 32); + } +} diff --git a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java index 204803a32..e794ab1a8 100644 --- a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java @@ -148,14 +148,15 @@ public class GlyfTableTestCase { private void setupSubsetReader(Map glyphs) throws IOException { TTFSubSetFile fontFile = new TTFSubSetFile(); - fontFile.readFont(originalFontReader, "Deja", glyphs); + String header = OFFontLoader.readHeader(subsetReader); + fontFile.readFont(originalFontReader, "Deja", header, glyphs); byte[] subsetFont = fontFile.getFontSubset(); InputStream intputStream = new ByteArrayInputStream(subsetFont); subsetReader = new FontFileReader(intputStream); } private void readLoca() throws IOException { - DirData loca = getTableData("loca"); + DirData loca = getTableData(OFTableName.LOCA.getName()); int numberOfGlyphs = (int) (loca.length - 4) / 4; glyphOffsets = new long[numberOfGlyphs]; subsetReader.seekSet(loca.offset); @@ -166,7 +167,7 @@ public class GlyfTableTestCase { } private int[] retrieveIndicesOfComposedGlyphs() throws IOException { - DirData glyf = getTableData("glyf"); + DirData glyf = getTableData(OFTableName.GLYF.getName()); int[] composedGlyphIndices = new int[glyphOffsets.length]; for (int i = 0; i < glyphOffsets.length; i++) { diff --git a/test/java/org/apache/fop/fonts/truetype/OTFFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/OTFFileTestCase.java new file mode 100644 index 000000000..737c2e05e --- /dev/null +++ b/test/java/org/apache/fop/fonts/truetype/OTFFileTestCase.java @@ -0,0 +1,86 @@ +/* + * 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.truetype; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class OTFFileTestCase { + protected OTFFile sourceSansProBold; + protected FontFileReader sourceSansReader = null; + protected OTFFile alexBrush; + protected FontFileReader alexBrushReader = null; + + /** + * Initializes fonts used for the testing of reading OTF CFF + * @throws IOException + */ + @Before + public void setUp() throws Exception { + sourceSansProBold = new OTFFile(); + InputStream sourceSansStream = new FileInputStream("test/resources/fonts/otf/SourceSansProBold.otf"); + sourceSansReader = new FontFileReader(sourceSansStream); + String sourceSansHeader = OFFontLoader.readHeader(sourceSansReader); + sourceSansProBold.readFont(sourceSansReader, sourceSansHeader); + sourceSansStream.close(); + + InputStream alexBrushStream = new FileInputStream("test/resources/fonts/otf/AlexBrushRegular.otf"); + alexBrush = new OTFFile(); + alexBrushReader = new FontFileReader(alexBrushStream); + String carolynaHeader = OFFontLoader.readHeader(alexBrushReader); + alexBrush.readFont(alexBrushReader, carolynaHeader); + alexBrushStream.close(); + } + + /** + * Tests the font names being read from the file + */ + @Test + public void testFontNames() { + assertTrue(sourceSansProBold.getFamilyNames().contains("Source Sans Pro")); + assertTrue(alexBrush.getFamilyNames().contains("Alex Brush")); + } + + /** + * Tests the number of glyphs and a select number of widths from each font + */ + @Test + public void testGlyphNumberAndWidths() { + assertEquals(824, sourceSansProBold.numberOfGlyphs); + assertEquals(256, alexBrush.numberOfGlyphs); + + int[] gids = {32, 42, 44, 47}; + int[] sourceSansWidths = {516, 555, 572, 383}; + for (int i = 0;i < gids.length;i++) { + assertEquals(sourceSansWidths[i], sourceSansProBold.getWidths()[gids[i]]); + } + int[] carolynaWidths = {842, 822, 658, 784}; + for (int i = 0;i < gids.length;i++) { + assertEquals(carolynaWidths[i], alexBrush.getWidths()[gids[i]]); + } + } +} diff --git a/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java new file mode 100644 index 000000000..604cca3a8 --- /dev/null +++ b/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java @@ -0,0 +1,148 @@ +/* + * 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.truetype; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.fontbox.cff.CFFDataInput; +import org.apache.fontbox.cff.CFFFont; +import org.apache.fontbox.cff.CFFParser; +import org.apache.fontbox.cff.IndexData; +import org.apache.fontbox.cff.Type2CharStringParser; + +import org.apache.fop.fonts.cff.CFFDataReader; +import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData; +import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class OTFSubSetFileTestCase extends OTFFileTestCase { + + CFFDataReader cffReaderSourceSans; + private OTFSubSetFile sourceSansSubset; + private byte[] sourceSansData; + CFFDataReader cffReaderHeitiStd; + + public OTFSubSetFileTestCase() throws IOException { + super(); + } + + /** + * Initialises the test by creating the font subset. A CFFDataReader is + * also created based on the subset data for use in the tests. + * @throws IOException + */ + @Before + public void setUp() throws Exception { + super.setUp(); + + Map glyphs = new HashMap(); + for (int i = 0; i < 256; i++) { + glyphs.put(i, i); + } + + sourceSansSubset = new OTFSubSetFile(); + String sourceSansHeader = OFFontLoader.readHeader(sourceSansReader); + sourceSansSubset.readFont(sourceSansReader, "SourceSansProBold", sourceSansHeader, glyphs); + sourceSansData = sourceSansSubset.getFontSubset(); + cffReaderSourceSans = new CFFDataReader(sourceSansData); + } + + /** + * Validates the CharString data against the original font + * @throws IOException + */ + @Test + public void testCharStringIndex() throws IOException { + assertEquals(256, cffReaderSourceSans.getCharStringIndex().getNumObjects()); + assertTrue(checkCorrectOffsets(cffReaderSourceSans.getCharStringIndex())); + validateCharStrings(cffReaderSourceSans); + } + + private boolean checkCorrectOffsets(CFFIndexData indexData) { + int last = 0; + for (int i = 0;i < indexData.getOffsets().length;i++) { + if (indexData.getOffsets()[i] < last) { + return false; + } + last = indexData.getOffsets()[i]; + } + return true; + } + + private void validateCharStrings(CFFDataReader cffReader) throws IOException { + CFFFont sourceSansOriginal = sourceSansProBold.fileFont; + Map origCharStringData = sourceSansOriginal.getCharStringsDict(); + IndexData origGlobalIndex = sourceSansOriginal.getGlobalSubrIndex(); + IndexData origLocalIndex = sourceSansOriginal.getLocalSubrIndex(); + + CFFDataInput globalSubrs = new CFFDataInput(cffReader.getGlobalIndexSubr().getByteData()); + CFFDataInput localSubrs = new CFFDataInput(cffReader.getLocalIndexSubr().getByteData()); + + IndexData globalIndexData = CFFParser.readIndexData(globalSubrs); + IndexData localIndexData = CFFParser.readIndexData(localSubrs); + + CFFIndexData charStrings = cffReader.getCharStringIndex(); + for (int i = 0;i < charStrings.getNumObjects();i++) { + byte[] charData = charStrings.getValue(i); + Type2CharStringParser parser = new Type2CharStringParser(); + + byte[] origCharData = origCharStringData.get(origCharStringData.keySet().toArray( + new String[0])[i]); + List origSeq = parser.parse(origCharData, origGlobalIndex, origLocalIndex); + + List subsetSeq = parser.parse(charData, globalIndexData, localIndexData); + + //Validates the subset glyph render routines against the originals + assertEquals(origSeq, subsetSeq); + } + } + + /** + * Validates the String index data and size + * @throws IOException + */ + @Test + public void testStringIndex() throws IOException { + assertEquals(164, cffReaderSourceSans.getStringIndex().getNumObjects()); + assertTrue(checkCorrectOffsets(cffReaderSourceSans.getStringIndex())); + assertEquals("Amacron", new String(cffReaderSourceSans.getStringIndex().getValue(5))); + assertEquals("Edotaccent", new String(cffReaderSourceSans.getStringIndex().getValue(32))); + assertEquals("uni0122", new String(cffReaderSourceSans.getStringIndex().getValue(45))); + } + + /** + * Validates the Top Dict data + * @throws IOException + */ + @Test + public void testTopDictData() throws IOException { + Map topDictEntries = cffReaderSourceSans.parseDictData( + cffReaderSourceSans.getTopDictIndex().getData()); + assertEquals(10, topDictEntries.size()); + } +} diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java index e04347032..a390bef0f 100644 --- a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java @@ -26,7 +26,7 @@ import java.util.Map; import org.junit.Test; -import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; +import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -55,14 +55,16 @@ public class TTFFileTestCase { dejavuTTFFile = new TTFFile(); InputStream dejaStream = new FileInputStream("test/resources/fonts/ttf/DejaVuLGCSerif.ttf"); dejavuReader = new FontFileReader(dejaStream); - dejavuTTFFile.readFont(dejavuReader); + String dejavuHeader = OFFontLoader.readHeader(dejavuReader); + dejavuTTFFile.readFont(dejavuReader, dejavuHeader); dejaStream.close(); InputStream droidStream = new FileInputStream("test/resources/fonts/ttf/DroidSansMono.ttf"); droidmonoTTFFile = new TTFFile(); droidmonoReader = new FontFileReader(droidStream); - droidmonoTTFFile.readFont(droidmonoReader); + String droidmonoHeader = OFFontLoader.readHeader(droidmonoReader); + droidmonoTTFFile.readFont(droidmonoReader, droidmonoHeader); droidStream.close(); } @@ -93,8 +95,10 @@ public class TTFFileTestCase { @Test public void testCheckTTC() throws IOException { // DejaVu is not a TTC, thus this returns true - assertTrue(dejavuTTFFile.checkTTC("")); - assertTrue(droidmonoTTFFile.checkTTC("")); + String dejavuHeader = OFFontLoader.readHeader(dejavuReader); + assertTrue(dejavuTTFFile.checkTTC(dejavuHeader, "")); + String droidmonoHeader = OFFontLoader.readHeader(droidmonoReader); + assertTrue(droidmonoTTFFile.checkTTC(droidmonoHeader, "")); /* * Cannot reasonably test the rest of this method without an actual truetype collection * because all methods in FontFileReader are "final" and thus mocking isn't possible. diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java index 063d8c781..f50b116ac 100644 --- a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java @@ -34,7 +34,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** - * Test case for {@link TTFFontLoader}. + * Test case for {@link OFFontLoader}. */ public class TTFFontLoaderTestCase { @@ -49,12 +49,12 @@ public class TTFFontLoaderTestCase { boolean embedded = false; boolean useKerning = true; - TTFFontLoader fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, + OFFontLoader fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver); assertTrue(fontLoader.getFont().hasKerningInfo()); useKerning = false; - fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO, + fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver); assertFalse(fontLoader.getFont().hasKerningInfo()); } diff --git a/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java index 16bedad8d..ef0dff5d5 100644 --- a/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java @@ -55,7 +55,8 @@ public class TTFSubSetFileTestCase extends TTFFileTestCase { for (int i = 0; i < 255; i++) { glyphs.put(i, i); } - ttfSubset.readFont(dejavuReader, "DejaVu", glyphs); + String header = OFFontLoader.readHeader(dejavuReader); + ttfSubset.readFont(dejavuReader, "DejaVu", header, glyphs); subset = ttfSubset.getFontSubset(); } /** @@ -68,7 +69,9 @@ public class TTFSubSetFileTestCase extends TTFFileTestCase { public void testReadFont3Args() throws IOException { ByteArrayInputStream byteArray = new ByteArrayInputStream(subset); - dejavuTTFFile.readFont(new FontFileReader(byteArray)); + FontFileReader reader = new FontFileReader(byteArray); + String header = OFFontLoader.readHeader(reader); + dejavuTTFFile.readFont(reader, header); // Test a couple arbitrary values assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), dejavuTTFFile.getFontBBox()[0]); assertEquals(dejavuTTFFile.getFullName(), "DejaVu LGC Serif"); diff --git a/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java index b9066dc2d..ac5ab3ddc 100644 --- a/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java @@ -37,37 +37,37 @@ public class TTFTableNameTestCase { */ @Test public void testGetName() throws IllegalAccessException { - assertEquals("tableDirectory", TTFTableName.TABLE_DIRECTORY.getName()); - assertEquals("EBDT", TTFTableName.EBDT.getName()); - assertEquals("EBLC", TTFTableName.EBLC.getName()); - assertEquals("EBSC", TTFTableName.EBSC.getName()); - assertEquals("FFTM", TTFTableName.FFTM.getName()); - assertEquals("GDEF", TTFTableName.GDEF.getName()); - assertEquals("GPOS", TTFTableName.GPOS.getName()); - assertEquals("GSUB", TTFTableName.GSUB.getName()); - assertEquals("LTSH", TTFTableName.LTSH.getName()); - assertEquals("OS/2", TTFTableName.OS2.getName()); - assertEquals("PCLT", TTFTableName.PCLT.getName()); - assertEquals("VDMX", TTFTableName.VDMX.getName()); - assertEquals("cmap", TTFTableName.CMAP.getName()); - assertEquals("cvt ", TTFTableName.CVT.getName()); - assertEquals("fpgm", TTFTableName.FPGM.getName()); - assertEquals("gasp", TTFTableName.GASP.getName()); - assertEquals("glyf", TTFTableName.GLYF.getName()); - assertEquals("hdmx", TTFTableName.HDMX.getName()); - assertEquals("head", TTFTableName.HEAD.getName()); - assertEquals("hhea", TTFTableName.HHEA.getName()); - assertEquals("hmtx", TTFTableName.HMTX.getName()); - assertEquals("kern", TTFTableName.KERN.getName()); - assertEquals("loca", TTFTableName.LOCA.getName()); - assertEquals("maxp", TTFTableName.MAXP.getName()); - assertEquals("name", TTFTableName.NAME.getName()); - assertEquals("post", TTFTableName.POST.getName()); - assertEquals("prep", TTFTableName.PREP.getName()); - assertEquals("vhea", TTFTableName.VHEA.getName()); - assertEquals("vmtx", TTFTableName.VMTX.getName()); + assertEquals("tableDirectory", OFTableName.TABLE_DIRECTORY.getName()); + assertEquals("EBDT", OFTableName.EBDT.getName()); + assertEquals("EBLC", OFTableName.EBLC.getName()); + assertEquals("EBSC", OFTableName.EBSC.getName()); + assertEquals("FFTM", OFTableName.FFTM.getName()); + assertEquals("GDEF", OFTableName.GDEF.getName()); + assertEquals("GPOS", OFTableName.GPOS.getName()); + assertEquals("GSUB", OFTableName.GSUB.getName()); + assertEquals("LTSH", OFTableName.LTSH.getName()); + assertEquals("OS/2", OFTableName.OS2.getName()); + assertEquals("PCLT", OFTableName.PCLT.getName()); + assertEquals("VDMX", OFTableName.VDMX.getName()); + assertEquals("cmap", OFTableName.CMAP.getName()); + assertEquals("cvt ", OFTableName.CVT.getName()); + assertEquals("fpgm", OFTableName.FPGM.getName()); + assertEquals("gasp", OFTableName.GASP.getName()); + assertEquals("glyf", OFTableName.GLYF.getName()); + assertEquals("hdmx", OFTableName.HDMX.getName()); + assertEquals("head", OFTableName.HEAD.getName()); + assertEquals("hhea", OFTableName.HHEA.getName()); + assertEquals("hmtx", OFTableName.HMTX.getName()); + assertEquals("kern", OFTableName.KERN.getName()); + assertEquals("loca", OFTableName.LOCA.getName()); + assertEquals("maxp", OFTableName.MAXP.getName()); + assertEquals("name", OFTableName.NAME.getName()); + assertEquals("post", OFTableName.POST.getName()); + assertEquals("prep", OFTableName.PREP.getName()); + assertEquals("vhea", OFTableName.VHEA.getName()); + assertEquals("vmtx", OFTableName.VMTX.getName()); // make sure it works with other table names - TTFTableName test = TTFTableName.getValue("test"); + OFTableName test = OFTableName.getValue("test"); assertEquals("test", test.getName()); } @@ -78,34 +78,34 @@ public class TTFTableNameTestCase { */ @Test public void testGetValue() throws IllegalAccessException { - assertEquals(TTFTableName.EBDT, TTFTableName.getValue("EBDT")); - assertEquals(TTFTableName.EBLC, TTFTableName.getValue("EBLC")); - assertEquals(TTFTableName.EBSC, TTFTableName.getValue("EBSC")); - assertEquals(TTFTableName.FFTM, TTFTableName.getValue("FFTM")); - assertEquals(TTFTableName.LTSH, TTFTableName.getValue("LTSH")); - assertEquals(TTFTableName.OS2, TTFTableName.getValue("OS/2")); - assertEquals(TTFTableName.PCLT, TTFTableName.getValue("PCLT")); - assertEquals(TTFTableName.VDMX, TTFTableName.getValue("VDMX")); - assertEquals(TTFTableName.CMAP, TTFTableName.getValue("cmap")); - assertEquals(TTFTableName.CVT, TTFTableName.getValue("cvt ")); - assertEquals(TTFTableName.FPGM, TTFTableName.getValue("fpgm")); - assertEquals(TTFTableName.GASP, TTFTableName.getValue("gasp")); - assertEquals(TTFTableName.GLYF, TTFTableName.getValue("glyf")); - assertEquals(TTFTableName.HDMX, TTFTableName.getValue("hdmx")); - assertEquals(TTFTableName.HEAD, TTFTableName.getValue("head")); - assertEquals(TTFTableName.HHEA, TTFTableName.getValue("hhea")); - assertEquals(TTFTableName.HMTX, TTFTableName.getValue("hmtx")); - assertEquals(TTFTableName.KERN, TTFTableName.getValue("kern")); - assertEquals(TTFTableName.LOCA, TTFTableName.getValue("loca")); - assertEquals(TTFTableName.MAXP, TTFTableName.getValue("maxp")); - assertEquals(TTFTableName.NAME, TTFTableName.getValue("name")); - assertEquals(TTFTableName.POST, TTFTableName.getValue("post")); - assertEquals(TTFTableName.PREP, TTFTableName.getValue("prep")); - assertEquals(TTFTableName.VHEA, TTFTableName.getValue("vhea")); - assertEquals(TTFTableName.VMTX, TTFTableName.getValue("vmtx")); + assertEquals(OFTableName.EBDT, OFTableName.getValue("EBDT")); + assertEquals(OFTableName.EBLC, OFTableName.getValue("EBLC")); + assertEquals(OFTableName.EBSC, OFTableName.getValue("EBSC")); + assertEquals(OFTableName.FFTM, OFTableName.getValue("FFTM")); + assertEquals(OFTableName.LTSH, OFTableName.getValue("LTSH")); + assertEquals(OFTableName.OS2, OFTableName.getValue("OS/2")); + assertEquals(OFTableName.PCLT, OFTableName.getValue("PCLT")); + assertEquals(OFTableName.VDMX, OFTableName.getValue("VDMX")); + assertEquals(OFTableName.CMAP, OFTableName.getValue("cmap")); + assertEquals(OFTableName.CVT, OFTableName.getValue("cvt ")); + assertEquals(OFTableName.FPGM, OFTableName.getValue("fpgm")); + assertEquals(OFTableName.GASP, OFTableName.getValue("gasp")); + assertEquals(OFTableName.GLYF, OFTableName.getValue("glyf")); + assertEquals(OFTableName.HDMX, OFTableName.getValue("hdmx")); + assertEquals(OFTableName.HEAD, OFTableName.getValue("head")); + assertEquals(OFTableName.HHEA, OFTableName.getValue("hhea")); + assertEquals(OFTableName.HMTX, OFTableName.getValue("hmtx")); + assertEquals(OFTableName.KERN, OFTableName.getValue("kern")); + assertEquals(OFTableName.LOCA, OFTableName.getValue("loca")); + assertEquals(OFTableName.MAXP, OFTableName.getValue("maxp")); + assertEquals(OFTableName.NAME, OFTableName.getValue("name")); + assertEquals(OFTableName.POST, OFTableName.getValue("post")); + assertEquals(OFTableName.PREP, OFTableName.getValue("prep")); + assertEquals(OFTableName.VHEA, OFTableName.getValue("vhea")); + assertEquals(OFTableName.VMTX, OFTableName.getValue("vmtx")); // Test that we can store a random table name and it will not fail or throw an error. - TTFTableName test = TTFTableName.getValue("random"); - assertTrue(test instanceof TTFTableName); + OFTableName test = OFTableName.getValue("random"); + assertTrue(test instanceof OFTableName); } /** @@ -115,10 +115,10 @@ public class TTFTableNameTestCase { */ @Test public void testHashCode() throws IllegalAccessException { - TTFTableName a = TTFTableName.getValue("testObject"); - TTFTableName b = TTFTableName.getValue("testObject"); + OFTableName a = OFTableName.getValue("testObject"); + OFTableName b = OFTableName.getValue("testObject"); assertTrue(a.hashCode() == b.hashCode()); - TTFTableName c = TTFTableName.getValue("fail"); + OFTableName c = OFTableName.getValue("fail"); assertFalse(a.hashCode() == c.hashCode()); } @@ -134,15 +134,15 @@ public class TTFTableNameTestCase { @Test public void testEquals() throws IllegalAccessException { // Reflexivity - TTFTableName a = TTFTableName.getValue("test"); + OFTableName a = OFTableName.getValue("test"); assertTrue(a.equals(a)); // Symmetry - TTFTableName b = TTFTableName.getValue("test"); + OFTableName b = OFTableName.getValue("test"); assertTrue(a.equals(b)); assertTrue(b.equals(a)); // Transitivity (tested with symmetry) // Consistency (test that a == b is true and that a == c fails) - TTFTableName c = TTFTableName.getValue("fail"); + OFTableName c = OFTableName.getValue("fail"); for (int i = 0; i < 100; i++) { assertTrue(a.equals(b)); assertFalse(a.equals(c)); diff --git a/test/resources/fonts/otf/AlexBrushRegular.otf b/test/resources/fonts/otf/AlexBrushRegular.otf new file mode 100644 index 000000000..2d48ec13d Binary files /dev/null and b/test/resources/fonts/otf/AlexBrushRegular.otf differ diff --git a/test/resources/fonts/otf/SourceSansProBold.otf b/test/resources/fonts/otf/SourceSansProBold.otf new file mode 100644 index 000000000..8998f3de6 Binary files /dev/null and b/test/resources/fonts/otf/SourceSansProBold.otf differ -- cgit v1.2.3 From 99e4e25414066866d0f8b3632623576246ae080b Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 14 Aug 2013 22:42:28 +0000 Subject: FOP-2252: OpenType CFF support for FOP; patch submitted by Robert Meyer (eclipse misbehaved and missed a file) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1514079 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fonts/cff/CFFDataReader.java | 927 +++++++++++++++++++++ src/java/org/apache/fop/pdf/PDFEncryptionJCE.java | 99 +-- 2 files changed, 937 insertions(+), 89 deletions(-) create mode 100644 src/java/org/apache/fop/fonts/cff/CFFDataReader.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/cff/CFFDataReader.java b/src/java/org/apache/fop/fonts/cff/CFFDataReader.java new file mode 100644 index 000000000..66126fb84 --- /dev/null +++ b/src/java/org/apache/fop/fonts/cff/CFFDataReader.java @@ -0,0 +1,927 @@ +/* + * 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.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.fontbox.cff.CFFDataInput; +import org.apache.fontbox.cff.CFFOperator; + +import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.OTFFile; + +/** + * A class to read the CFF data from an OTF CFF font file. + */ +public class CFFDataReader { + private CFFDataInput cffData; + + private byte[] header; + private CFFIndexData nameIndex; + private CFFIndexData topDICTIndex; + private CFFIndexData stringIndex; + private CFFIndexData charStringIndex; + private CFFIndexData globalIndexSubr; + private CFFIndexData localIndexSubr; + private CustomEncoding encoding; + private FDSelect fdSelect; + private List fdFonts; + + private static final int DOUBLE_BYTE_OPERATOR = 12; + private static final int NUM_STANDARD_STRINGS = 391; + + /** Commonly used parsed dictionaries */ + private LinkedHashMap topDict; + + public CFFDataReader() { + + } + + /** + * Constructor for the CFF data reader which accepts the CFF byte data + * as an argument. + * @param cffDataArray A byte array which holds the CFF data + */ + public CFFDataReader(byte[] cffDataArray) throws IOException { + cffData = new CFFDataInput(cffDataArray); + readCFFData(); + } + + /** + * Constructor for the CFF data reader which accepts a FontFileReader object + * which points to the original font file as an argument. + * @param fontFile The font file as represented by a FontFileReader object + */ + public CFFDataReader(FontFileReader fontFile) throws IOException { + cffData = new CFFDataInput(OTFFile.getCFFData(fontFile)); + readCFFData(); + } + + private void readCFFData() throws IOException { + header = readHeader(); + nameIndex = readIndex(); + topDICTIndex = readIndex(); + topDict = parseDictData(topDICTIndex.getData()); + stringIndex = readIndex(); + globalIndexSubr = readIndex(); + charStringIndex = readCharStringIndex(); + encoding = readEncoding(); + fdSelect = readFDSelect(); + localIndexSubr = readLocalIndexSubrs(); + fdFonts = parseCIDData(); + } + + public Map getPrivateDict(DICTEntry privateEntry) throws IOException { + return parseDictData(getPrivateDictBytes(privateEntry)); + } + + public byte[] getPrivateDictBytes(DICTEntry privateEntry) throws IOException { + int privateLength = privateEntry.getOperands().get(0).intValue(); + int privateOffset = privateEntry.getOperands().get(1).intValue(); + return getCFFOffsetBytes(privateOffset, privateLength); + } + + /** + * Retrieves a number of bytes from the CFF data stream + * @param offset The offset of the bytes to retrieve + * @param length The number of bytes to retrieve + * @return Returns a byte array of requested bytes + * @throws IOException Throws an IO Exception if an error occurs + */ + private byte[] getCFFOffsetBytes(int offset, int length) throws IOException { + cffData.setPosition(offset); + return cffData.readBytes(length); + } + + /** + * Parses the dictionary data and returns a map of objects for each entry + * @param dictData The data for the dictionary data + * @return Returns a map of type DICTEntry identified by the operand name + * @throws IOException Throws an IO Exception if an error occurs + */ + public LinkedHashMap parseDictData(byte[] dictData) throws IOException { + LinkedHashMap dictEntries = new LinkedHashMap(); + List operands = new ArrayList(); + List operandLengths = new ArrayList(); + int lastOperandLength = 0; + for (int i = 0; i < dictData.length; i++) { + int readByte = dictData[i] & 0xFF; + if (readByte < 28) { + int[] operator = new int[(readByte == DOUBLE_BYTE_OPERATOR) ? 2 : 1]; + if (readByte == DOUBLE_BYTE_OPERATOR) { + operator[0] = dictData[i]; + operator[1] = dictData[i + 1]; + i++; + } else { + operator[0] = dictData[i]; + } + String operatorName = ""; + CFFOperator tempOp = null; + if (operator.length > 1) { + tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0], operator[1])); + } else { + tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0])); + } + if (tempOp != null) { + operatorName = tempOp.getName(); + } + DICTEntry newEntry = new DICTEntry(); + newEntry.setOperator(operator); + newEntry.setOperands(new ArrayList(operands)); + newEntry.setOperatorName(operatorName); + newEntry.setOffset(i - lastOperandLength); + newEntry.setOperandLength(lastOperandLength); + newEntry.setOperandLengths(new ArrayList(operandLengths)); + byte[] byteData = new byte[lastOperandLength + operator.length]; + System.arraycopy(dictData, i - operator.length - (lastOperandLength - 1), + byteData, 0, operator.length + lastOperandLength); + newEntry.setByteData(byteData); + dictEntries.put(operatorName, newEntry); + operands.clear(); + operandLengths.clear(); + lastOperandLength = 0; + } else { + if (readByte >= 32 && readByte <= 246) { + operands.add(readByte - 139); + lastOperandLength += 1; + operandLengths.add(1); + } else if (readByte >= 247 && readByte <= 250) { + operands.add((readByte - 247) * 256 + (dictData[i + 1] & 0xFF) + 108); + lastOperandLength += 2; + operandLengths.add(2); + i++; + } else if (readByte >= 251 && readByte <= 254) { + operands.add(-(readByte - 251) * 256 - (dictData[i + 1] & 0xFF) - 108); + lastOperandLength += 2; + operandLengths.add(2); + i++; + } else if (readByte == 28) { + operands.add((dictData[i + 1] & 0xFF) << 8 | (dictData[i + 2] & 0xFF)); + lastOperandLength += 3; + operandLengths.add(3); + i += 2; + } else if (readByte == 29) { + operands.add((dictData[i + 1] & 0xFF) << 24 | (dictData[i + 2] & 0xFF) << 16 + | (dictData[i + 3] & 0xFF) << 8 | (dictData[i + 4] & 0xFF)); + lastOperandLength += 5; + operandLengths.add(5); + i += 4; + } else if (readByte == 30) { + boolean terminatorFound = false; + StringBuilder realNumber = new StringBuilder(); + int byteCount = 1; + do { + byte nibblesByte = dictData[++i]; + byteCount++; + terminatorFound = readNibble(realNumber, (nibblesByte >> 4) & 0x0F); + if (!terminatorFound) { + terminatorFound = readNibble(realNumber, nibblesByte & 0x0F); + } + } while (!terminatorFound); + operands.add(Double.valueOf(realNumber.toString())); + lastOperandLength += byteCount; + operandLengths.add(byteCount); + } + } + } + return dictEntries; + } + + private boolean readNibble(StringBuilder realNumber, int nibble) { + if (nibble <= 0x9) { + realNumber.append(nibble); + } else { + switch (nibble) { + case 0xa: realNumber.append("."); break; + case 0xb: realNumber.append("E"); break; + case 0xc: realNumber.append("E-"); break; + case 0xd: break; + case 0xe: realNumber.append("-"); break; + case 0xf: return true; + default: throw new AssertionError("Unexpected nibble value"); + } + } + return false; + } + + /** + * A class containing data for a dictionary entry + */ + public static class DICTEntry { + private int[] operator; + private List operands; + private List operandLengths; + private String operatorName; + private int offset; + private int operandLength; + private byte[] data = new byte[0]; + + public void setOperator(int[] operator) { + this.operator = operator; + } + + public int[] getOperator() { + return this.operator; + } + + public void setOperands(List operands) { + this.operands = operands; + } + + public List getOperands() { + return this.operands; + } + + public void setOperatorName(String operatorName) { + this.operatorName = operatorName; + } + + public String getOperatorName() { + return this.operatorName; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getOffset() { + return this.offset; + } + + public void setOperandLength(int operandLength) { + this.operandLength = operandLength; + } + + public int getOperandLength() { + return this.operandLength; + } + + public void setByteData(byte[] data) { + this.data = data.clone(); + } + + public byte[] getByteData() { + return data.clone(); + } + + public void setOperandLengths(List operandLengths) { + this.operandLengths = operandLengths; + } + + public List getOperandLengths() { + return operandLengths; + } + } + + private byte[] readHeader() throws IOException { + //Read known header + byte[] fixedHeader = cffData.readBytes(4); + int hdrSize = (fixedHeader[2] & 0xFF); + byte[] extra = cffData.readBytes(hdrSize - 4); + byte[] header = new byte[hdrSize]; + for (int i = 0; i < fixedHeader.length; i++) { + header[i] = fixedHeader[i]; + } + for (int i = 4; i < extra.length; i++) { + header[i] = extra[i - 4]; + } + return header; + } + + /** + * Reads a CFF index object are the specified offset position + * @param offset The position of the index object to read + * @return Returns an object representing the index + * @throws IOException Throws an IO Exception if an error occurs + */ + public CFFIndexData readIndex(int offset) throws IOException { + cffData.setPosition(offset); + return readIndex(); + } + + private CFFIndexData readIndex() throws IOException { + return readIndex(cffData); + } + + /** + * Reads an index from the current position of the CFFDataInput object + * @param input The object holding the CFF byte data + * @return Returns an object representing the index + * @throws IOException Throws an IO Exception if an error occurs + */ + public CFFIndexData readIndex(CFFDataInput input) throws IOException { + CFFIndexData nameIndex = new CFFIndexData(); + if (input != null) { + int origPos = input.getPosition(); + nameIndex.parseIndexHeader(input); + int tableSize = input.getPosition() - origPos; + nameIndex.setByteData(input.getPosition() - tableSize, tableSize); + } + return nameIndex; + } + + /** + * Retrieves the SID for the given GID object + * @param charsetOffset The offset of the charset data + * @param GID The GID for which to retrieve the SID + * @return Returns the SID as an integer + */ + public int getSIDFromGID(int charsetOffset, int gid) throws IOException { + if (gid == 0) { + return 0; + } + cffData.setPosition(charsetOffset); + int charsetFormat = cffData.readCard8(); + switch (charsetFormat) { + case 0: //Adjust for .notdef character + cffData.setPosition(cffData.getPosition() + (--gid * 2)); + return cffData.readSID(); + case 1: return getSIDFromGIDFormat(gid, 1); + case 2: return getSIDFromGIDFormat(gid, 2); + default: return 0; + } + } + + private int getSIDFromGIDFormat(int gid, int format) throws IOException { + int glyphCount = 0; + while (true) { + int oldGlyphCount = glyphCount; + int start = cffData.readSID(); + glyphCount += ((format == 1) ? cffData.readCard8() : cffData.readCard16()) + 1; + if (gid <= glyphCount) { + return start + (gid - oldGlyphCount) - 1; + } + } + } + + public byte[] getHeader() { + return header.clone(); + } + + public CFFIndexData getNameIndex() { + return nameIndex; + } + + public CFFIndexData getTopDictIndex() { + return topDICTIndex; + } + + public LinkedHashMap getTopDictEntries() { + return topDict; + } + + public CFFIndexData getStringIndex() { + return stringIndex; + } + + public CFFIndexData getGlobalIndexSubr() { + return globalIndexSubr; + } + + public CFFIndexData getLocalIndexSubr() { + return localIndexSubr; + } + + public CFFIndexData getCharStringIndex() { + return charStringIndex; + } + + public CFFDataInput getCFFData() { + return cffData; + } + + public CustomEncoding getEncoding() { + return encoding; + } + + public FDSelect getFDSelect() { + return fdSelect; + } + + public List getFDFonts() { + return fdFonts; + } + + public CFFDataInput getLocalSubrsForGlyph(int glyph) throws IOException { + //Subsets are currently written using a Format0 FDSelect + FDSelect fontDictionary = getFDSelect(); + if (fontDictionary instanceof Format0FDSelect) { + Format0FDSelect fdSelect = (Format0FDSelect)fontDictionary; + int found = fdSelect.getFDIndexes()[glyph]; + FontDict font = getFDFonts().get(found); + byte[] localSubrData = font.getLocalSubrData().getByteData(); + if (localSubrData != null) { + return new CFFDataInput(localSubrData); + } else { + return null; + } + } else if (fontDictionary instanceof Format3FDSelect) { + Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary; + int index = 0; + for (int first : fdSelect.getRanges().keySet()) { + if (first > glyph) { + break; + } + index++; + } + FontDict font = getFDFonts().get(index); + byte[] localSubrsData = font.getLocalSubrData().getByteData(); + if (localSubrsData != null) { + return new CFFDataInput(localSubrsData); + } else { + return null; + } + } + return null; + } + + /** + * Parses the char string index from the CFF byte data + * @param offset The offset to the char string index + * @return Returns the char string index object + * @throws IOException Throws an IO Exception if an error occurs + */ + public CFFIndexData readCharStringIndex() throws IOException { + int offset = topDict.get("CharStrings").getOperands().get(0).intValue(); + cffData.setPosition(offset); + return readIndex(); + } + + private CustomEncoding readEncoding() throws IOException { + CustomEncoding foundEncoding = null; + if (topDict.get("Encoding") != null) { + int offset = topDict.get("Encoding").getOperands().get(0).intValue(); + if (offset != 0 && offset != 1) { + //No need to set the offset as we are reading the data sequentially. + int format = cffData.readCard8(); + int numEntries = cffData.readCard8(); + switch (format) { + case 0: + foundEncoding = readFormat0Encoding(format, numEntries); + break; + case 1: + foundEncoding = readFormat1Encoding(format, numEntries); + break; + default: break; + } + } + } + return foundEncoding; + } + + private Format0Encoding readFormat0Encoding(int format, int numEntries) + throws IOException { + Format0Encoding newEncoding = new Format0Encoding(); + newEncoding.setFormat(format); + newEncoding.setNumEntries(numEntries); + int[] codes = new int[numEntries]; + for (int i = 0; i < numEntries; i++) { + codes[i] = cffData.readCard8(); + } + newEncoding.setCodes(codes); + return newEncoding; + } + + private Format1Encoding readFormat1Encoding(int format, int numEntries) + throws IOException { + Format1Encoding newEncoding = new Format1Encoding(); + newEncoding.setFormat(format); + newEncoding.setNumEntries(numEntries); + LinkedHashMap ranges = new LinkedHashMap(); + for (int i = 0; i < numEntries; i++) { + int first = cffData.readCard8(); + int left = cffData.readCard8(); + ranges.put(first, left); + } + newEncoding.setRanges(ranges); + return newEncoding; + } + + private FDSelect readFDSelect() throws IOException { + FDSelect fdSelect = null; + DICTEntry fdSelectEntry = topDict.get("FDSelect"); + if (fdSelectEntry != null) { + int fdOffset = fdSelectEntry.getOperands().get(0).intValue(); + cffData.setPosition(fdOffset); + int format = cffData.readCard8(); + switch (format) { + case 0: + fdSelect = readFormat0FDSelect(); + break; + case 3: + fdSelect = readFormat3FDSelect(); + break; + default: + } + } + return fdSelect; + } + + private Format0FDSelect readFormat0FDSelect() throws IOException { + Format0FDSelect newFDs = new Format0FDSelect(); + newFDs.setFormat(0); + int glyphCount = charStringIndex.getNumObjects(); + int[] fds = new int[glyphCount]; + for (int i = 0; i < glyphCount; i++) { + fds[i] = cffData.readCard8(); + } + newFDs.setFDIndexes(fds); + return newFDs; + } + + private Format3FDSelect readFormat3FDSelect() throws IOException { + Format3FDSelect newFDs = new Format3FDSelect(); + newFDs.setFormat(3); + int rangeCount = cffData.readCard16(); + newFDs.setRangeCount(rangeCount); + LinkedHashMap ranges = new LinkedHashMap(); + for (int i = 0; i < rangeCount; i++) { + int first = cffData.readCard16(); + int fd = cffData.readCard8(); + ranges.put(first, fd); + } + newFDs.setRanges(ranges); + newFDs.setSentinelGID(cffData.readCard16()); + return newFDs; + } + + private List parseCIDData() throws IOException { + ArrayList fdFonts = new ArrayList(); + if (topDict.get("ROS") != null) { + DICTEntry fdArray = topDict.get("FDArray"); + if (fdArray != null) { + int fdIndex = fdArray.getOperands().get(0).intValue(); + CFFIndexData fontDicts = readIndex(fdIndex); + for (int i = 0; i < fontDicts.getNumObjects(); i++) { + FontDict newFontDict = new FontDict(); + + byte[] fdData = fontDicts.getValue(i); + LinkedHashMap fdEntries = parseDictData(fdData); + newFontDict.setByteData(fontDicts.getValuePosition(i), fontDicts.getValueLength(i)); + DICTEntry fontFDEntry = fdEntries.get("FontName"); + newFontDict.setFontName(getString(fontFDEntry.getOperands().get(0).intValue())); + DICTEntry privateFDEntry = fdEntries.get("Private"); + if (privateFDEntry != null) { + newFontDict = setFDData(privateFDEntry, newFontDict); + } + + fdFonts.add(newFontDict); + } + } + } + return fdFonts; + } + + private FontDict setFDData(DICTEntry privateFDEntry, FontDict newFontDict) throws IOException { + int privateFDLength = privateFDEntry.getOperands().get(0).intValue(); + int privateFDOffset = privateFDEntry.getOperands().get(1).intValue(); + cffData.setPosition(privateFDOffset); + byte[] privateDict = cffData.readBytes(privateFDLength); + newFontDict.setPrivateDictData(privateFDOffset, privateFDLength); + LinkedHashMap privateEntries = parseDictData(privateDict); + DICTEntry subroutines = privateEntries.get("Subrs"); + if (subroutines != null) { + CFFIndexData localSubrs = readIndex(privateFDOffset + + subroutines.getOperands().get(0).intValue()); + newFontDict.setLocalSubrData(localSubrs); + } else { + newFontDict.setLocalSubrData(new CFFIndexData()); + } + return newFontDict; + } + + private String getString(int sid) throws IOException { + return new String(stringIndex.getValue(sid - NUM_STANDARD_STRINGS)); + } + + private CFFIndexData readLocalIndexSubrs() throws IOException { + CFFIndexData localSubrs = null; + DICTEntry privateEntry = topDict.get("Private"); + if (privateEntry != null) { + int length = privateEntry.getOperands().get(0).intValue(); + int offset = privateEntry.getOperands().get(1).intValue(); + cffData.setPosition(offset); + byte[] privateData = cffData.readBytes(length); + LinkedHashMap privateDict = parseDictData(privateData); + DICTEntry localSubrsEntry = privateDict.get("Subrs"); + if (localSubrsEntry != null) { + int localOffset = offset + localSubrsEntry.getOperands().get(0).intValue(); + cffData.setPosition(localOffset); + localSubrs = readIndex(); + } + } + return localSubrs; + } + + /** + * Parent class which provides the ability to retrieve byte data from + * a sub-table. + */ + public class CFFSubTable { + private DataLocation dataLocation = new DataLocation(); + + public void setByteData(int position, int length) { + dataLocation = new DataLocation(position, length); + } + + public byte[] getByteData() throws IOException { + int oldPos = cffData.getPosition(); + try { + cffData.setPosition(dataLocation.getDataPosition()); + return cffData.readBytes(dataLocation.getDataLength()); + } finally { + cffData.setPosition(oldPos); + } + } + } + + /** + * An object used to hold index data from the CFF data + */ + public class CFFIndexData extends CFFSubTable { + private int numObjects; + private int offSize; + private int[] offsets = new int[0]; + private DataLocation dataLocation = new DataLocation(); + + public void setNumObjects(int numObjects) { + this.numObjects = numObjects; + } + + public int getNumObjects() { + return this.numObjects; + } + + public void setOffSize(int offSize) { + this.offSize = offSize; + } + + public int getOffSize() { + return this.offSize; + } + + public void setOffsets(int[] offsets) { + this.offsets = offsets.clone(); + } + + public int[] getOffsets() { + return offsets.clone(); + } + + public void setData(int position, int length) { + dataLocation = new DataLocation(position, length); + } + + public byte[] getData() throws IOException { + int origPos = cffData.getPosition(); + try { + cffData.setPosition(dataLocation.getDataPosition()); + return cffData.readBytes(dataLocation.getDataLength()); + } finally { + cffData.setPosition(origPos); + } + } + + /** + * Parses index data from an index object found within the CFF byte data + * @param cffData A byte array containing the CFF data + * @throws IOException Throws an IO Exception if an error occurs + */ + public void parseIndexHeader(CFFDataInput cffData) throws IOException { + setNumObjects(cffData.readCard16()); + setOffSize(cffData.readOffSize()); + int[] offsets = new int[getNumObjects() + 1]; + byte[] bytes; + //Fills the offsets array + for (int i = 0; i <= getNumObjects(); i++) { + switch (getOffSize()) { + case 1: + offsets[i] = cffData.readCard8(); + break; + case 2: + offsets[i] = cffData.readCard16(); + break; + case 3: + bytes = cffData.readBytes(3); + offsets[i] = ((bytes[0] & 0xFF) << 16) + ((bytes[1] & 0xFF) << 8) + (bytes[2] & 0xFF); + break; + case 4: + bytes = cffData.readBytes(4); + offsets[i] = ((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16) + + ((bytes[2] & 0xFF) << 8) + (bytes[3] & 0xFF); + break; + default: continue; + } + } + setOffsets(offsets); + int position = cffData.getPosition(); + int dataSize = offsets[offsets.length - 1] - offsets[0]; + + cffData.setPosition(cffData.getPosition() + dataSize); + setData(position, dataSize); + } + + /** + * Retrieves data from the index data + * @param index The index position of the data to retrieve + * @return Returns the byte data for the given index + * @throws IOException Throws an IO Exception if an error occurs + */ + public byte[] getValue(int index) throws IOException { + int oldPos = cffData.getPosition(); + try { + cffData.setPosition(dataLocation.getDataPosition() + (offsets[index] - 1)); + return cffData.readBytes(offsets[index + 1] - offsets[index]); + } finally { + cffData.setPosition(oldPos); + } + } + + public int getValuePosition(int index) { + return dataLocation.getDataPosition() + (offsets[index] - 1); + } + + public int getValueLength(int index) { + return offsets[index + 1] - offsets[index]; + } + } + + public abstract class CustomEncoding { + private int format; + private int numEntries; + + public void setFormat(int format) { + this.format = format; + } + + public int getFormat() { + return format; + } + + public void setNumEntries(int numEntries) { + this.numEntries = numEntries; + } + + public int getNumEntries() { + return numEntries; + } + } + + public class Format0Encoding extends CustomEncoding { + private int[] codes = new int[0]; + + public void setCodes(int[] codes) { + this.codes = codes.clone(); + } + + public int[] getCodes() { + return codes.clone(); + } + } + + public class Format1Encoding extends CustomEncoding { + private LinkedHashMap ranges; + + public void setRanges(LinkedHashMap ranges) { + this.ranges = ranges; + } + + public LinkedHashMap getRanges() { + return ranges; + } + } + + public class FDSelect { + private int format; + + public void setFormat(int format) { + this.format = format; + } + + public int getFormat() { + return format; + } + } + + public class Format0FDSelect extends FDSelect { + private int[] fds = new int[0]; + + public void setFDIndexes(int[] fds) { + this.fds = fds.clone(); + } + + public int[] getFDIndexes() { + return fds.clone(); + } + } + + public class Format3FDSelect extends FDSelect { + private int rangeCount; + private LinkedHashMap ranges; + private int sentinelGID; + + public void setRangeCount(int rangeCount) { + this.rangeCount = rangeCount; + } + + public int getRangeCount() { + return rangeCount; + } + + public void setRanges(LinkedHashMap ranges) { + this.ranges = ranges; + } + + public LinkedHashMap getRanges() { + return ranges; + } + + public void setSentinelGID(int sentinelGID) { + this.sentinelGID = sentinelGID; + } + + public int getSentinelGID() { + return sentinelGID; + } + } + + public class FontDict extends CFFSubTable { + private String fontName; + private DataLocation dataLocation = new DataLocation(); + private CFFIndexData localSubrData; + + public void setFontName(String groupName) { + this.fontName = groupName; + } + + public String getFontName() { + return fontName; + } + + public void setPrivateDictData(int position, int length) { + dataLocation = new DataLocation(position, length); + } + + public byte[] getPrivateDictData() throws IOException { + int origPos = cffData.getPosition(); + try { + cffData.setPosition(dataLocation.getDataPosition()); + return cffData.readBytes(dataLocation.getDataLength()); + } finally { + cffData.setPosition(origPos); + } + } + + public void setLocalSubrData(CFFIndexData localSubrData) { + this.localSubrData = localSubrData; + } + + public CFFIndexData getLocalSubrData() { + return localSubrData; + } + } + + private static class DataLocation { + private int dataPosition; + private int dataLength; + + public DataLocation() { + dataPosition = 0; + dataLength = 0; + } + + public DataLocation(int position, int length) { + this.dataPosition = position; + this.dataLength = length; + } + + public int getDataPosition() { + return dataPosition; + } + + public int getDataLength() { + return dataLength; + } + } +} diff --git a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java index 8fd8d3444..2fe9f29e0 100644 --- a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java +++ b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java @@ -449,13 +449,14 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { private class Rev5Engine extends InitializationEngine { - protected byte[] userValidationSalt = new byte[8]; - protected byte[] userKeySalt = new byte[8]; - protected byte[] ownerValidationSalt = new byte[8]; - protected byte[] ownerKeySalt = new byte[8]; - protected byte[] ueValue; - protected byte[] oeValue; - protected final boolean encryptMetadata; + // private SecureRandom random = new SecureRandom(); + private byte[] userValidationSalt = new byte[8]; + private byte[] userKeySalt = new byte[8]; + private byte[] ownerValidationSalt = new byte[8]; + private byte[] ownerKeySalt = new byte[8]; + private byte[] ueValue; + private byte[] oeValue; + private final boolean encryptMetadata; Rev5Engine(EncryptionSettings encryptionSettings) { super(encryptionSettings); @@ -562,7 +563,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { /** * Algorithm 3.8-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) */ - protected void computeUEValue() { + private void computeUEValue() { digest.reset(); byte[] prepared = preparedUserPassword; byte[] concatenated = new byte[prepared.length + 8]; @@ -576,7 +577,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { /** * Algorithm 3.9-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) */ - protected void computeOEValue() { + private void computeOEValue() { digest.reset(); byte[] prepared = preparedOwnerPassword; byte[] concatenated = new byte[prepared.length + 56]; @@ -614,86 +615,6 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { } } - private class Rev6Engine extends Rev5Engine { - - private MessageDigest digest384; - private MessageDigest digest512; - - Rev6Engine(EncryptionSettings encryptionSettings) { - super(encryptionSettings); - try { - digest384 = MessageDigest.getInstance("SHA-384"); - digest512 = MessageDigest.getInstance("SHA-512"); - } catch (NoSuchAlgorithmException e) { - throw new UnsupportedOperationException(e.getMessage()); - } - } - - @Override - protected void computeUValue() { - byte[] userBytes = new byte[16]; - random.nextBytes(userBytes); - System.arraycopy(userBytes, 0, userValidationSalt, 0, 8); - System.arraycopy(userBytes, 8, userKeySalt, 0, 8); - digest.reset(); - byte[] prepared = preparedUserPassword; - byte[] concatenated = new byte[prepared.length + 8]; - System.arraycopy(prepared, 0, concatenated, 0, prepared.length); - System.arraycopy(userValidationSalt, 0, concatenated, prepared.length, 8); - digest.update(concatenated); - byte[] block = digest.digest(); - int blockSize = 32; - byte[] key = new byte[16]; - byte[] iv = new byte[16]; - int length = prepared.length + blockSize; - byte[] data = new byte[length * 64]; - for (int i = 0; i < 64 || i < data[length * 64 - 1] + 32; i++) { - System.arraycopy(block, 0, key, 0, 16); - System.arraycopy(block, 16, iv, 0, 16); - for (int j = 0; j < 64; j++) { - System.arraycopy(prepared, 0, data, j * length, prepared.length); - System.arraycopy(block, 0, data, j * length + prepared.length, blockSize); - } - try { - final Cipher cipher = PDFEncryptionJCE.initCipher(key, false, iv); - data = cipher.doFinal(data); - } catch (IllegalBlockSizeException e) { - throw new IllegalStateException(e.getMessage()); - } catch (BadPaddingException e) { - throw new IllegalStateException(e.getMessage()); - } - int sum = 0; - for (int k = 0; k < 16; k++) { - sum += data[k]; - } - blockSize = 32 + (sum % 3) * 16; - switch (blockSize) { - case 32: - digest.reset(); - digest.update(data); - block = digest.digest(); - break; - case 48: - digest384.reset(); - digest384.update(data); - block = digest384.digest(); - break; - case 64: - digest512.reset(); - digest512.update(data); - block = digest512.digest(); - break; - default: - // not possible - break; - } - length = prepared.length + blockSize; - data = new byte[length * 64]; - } - } - - } - private class EncryptionFilter extends PDFFilter { private int streamNumber; -- cgit v1.2.3 From 119c49e73ecda40e92303c03231086eae61295df Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Tue, 20 Aug 2013 15:00:13 +0000 Subject: FOP-2275: Quadratic Bezier curves not properly rendered git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1515840 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/svg/PDFGraphics2D.java | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 526420d9c..efa71a225 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -1691,11 +1691,15 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @param iter PathIterator to process */ public void processPathIterator(PathIterator iter) { + double lastX = 0.0; + double lastY = 0.0; while (!iter.isDone()) { double[] vals = new double[6]; int type = iter.currentSegment(vals); switch (type) { case PathIterator.SEG_CUBICTO: + lastX = vals[4]; + lastY = vals[5]; currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " " + PDFNumber.doubleOut(vals[1], DEC) + " " + PDFNumber.doubleOut(vals[2], DEC) + " " @@ -1704,18 +1708,30 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand + PDFNumber.doubleOut(vals[5], DEC) + " c\n"); break; case PathIterator.SEG_LINETO: + lastX = vals[0]; + lastY = vals[1]; currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " " + PDFNumber.doubleOut(vals[1], DEC) + " l\n"); break; case PathIterator.SEG_MOVETO: + lastX = vals[0]; + lastY = vals[1]; currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " " + PDFNumber.doubleOut(vals[1], DEC) + " m\n"); break; case PathIterator.SEG_QUADTO: - currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " " - + PDFNumber.doubleOut(vals[1], DEC) + " " - + PDFNumber.doubleOut(vals[2], DEC) + " " - + PDFNumber.doubleOut(vals[3], DEC) + " y\n"); + double controlPointAX = lastX + ((2.0 / 3.0) * (vals[0] - lastX)); + double controlPointAY = lastY + ((2.0 / 3.0) * (vals[1] - lastY)); + double controlPointBX = vals[2] + ((2.0 / 3.0) * (vals[0] - vals[2])); + double controlPointBY = vals[3] + ((2.0 / 3.0) * (vals[1] - vals[3])); + currentStream.write(PDFNumber.doubleOut(controlPointAX, DEC) + " " + + PDFNumber.doubleOut(controlPointAY, DEC) + " " + + PDFNumber.doubleOut(controlPointBX, DEC) + " " + + PDFNumber.doubleOut(controlPointBY, DEC) + " " + + PDFNumber.doubleOut(vals[2], DEC) + " " + + PDFNumber.doubleOut(vals[3], DEC) + " c\n"); + lastX = vals[2]; + lastY = vals[3]; break; case PathIterator.SEG_CLOSE: currentStream.write("h\n"); -- cgit v1.2.3 From 54eec84366117b4705a71810f1d1c87529c32ba5 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Fri, 13 Sep 2013 13:56:18 +0000 Subject: FOP-2298: Enable support for PDF Transitions by defining low-level mechanism to augment /Catalog and /Page dictionaries. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1522934 13f79535-47bb-0310-9956-ffa450edef68 --- findbugs-exclude.xml | 20 +++ .../org.apache.fop.util.ContentHandlerFactory | 1 + .../org/apache/fop/fo/extensions/ExtensionObj.java | 65 --------- src/java/org/apache/fop/pdf/PDFNumber.java | 8 ++ .../AbstractXMLWritingIFDocumentHandler.java | 4 +- .../apache/fop/render/intermediate/IFContext.java | 18 +++ .../apache/fop/render/intermediate/IFRenderer.java | 2 + .../fop/render/intermediate/IFSerializer.java | 2 + .../apache/fop/render/pdf/PDFDocumentHandler.java | 16 ++- .../apache/fop/render/pdf/PDFRenderingUtil.java | 52 ++++++- .../extensions/AbstractPDFDictionaryElement.java | 43 ++++++ .../extensions/AbstractPDFExtensionElement.java | 7 +- .../pdf/extensions/PDFDictionaryAttachment.java | 125 +++++++++++++++++ .../pdf/extensions/PDFDictionaryElement.java | 126 +++++++++++++++++ .../pdf/extensions/PDFDictionaryEntryElement.java | 109 ++++++++++++++ .../extensions/PDFDictionaryEntryExtension.java | 110 +++++++++++++++ .../pdf/extensions/PDFDictionaryEntryType.java | 56 ++++++++ .../pdf/extensions/PDFDictionaryExtension.java | 124 ++++++++++++++++ .../render/pdf/extensions/PDFDictionaryType.java | 54 +++++++ .../render/pdf/extensions/PDFElementMapping.java | 36 ++++- .../pdf/extensions/PDFEmbeddedFileAttachment.java | 156 +++++++++++++++++++++ .../pdf/extensions/PDFEmbeddedFileElement.java | 18 +-- .../PDFEmbeddedFileExtensionAttachment.java | 154 -------------------- .../pdf/extensions/PDFExtensionAttachment.java | 35 ++--- .../render/pdf/extensions/PDFExtensionHandler.java | 113 +++++++++++---- .../pdf/extensions/PDFExtensionHandlerFactory.java | 44 ++++++ .../apache/fop/tools/fontlist/FontListMain.java | 2 +- .../fop/util/GenerationHelperContentHandler.java | 13 +- src/java/org/apache/fop/util/XMLUtil.java | 31 ++++ .../apache/fop/render/svg/SVGDocumentHandler.java | 2 +- status.xml | 3 + .../pdf-dictionary-extension_1.xml | 90 ++++++++++++ 32 files changed, 1341 insertions(+), 298 deletions(-) delete mode 100644 src/java/org/apache/fop/fo/extensions/ExtensionObj.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml (limited to 'src/java/org/apache/fop') diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 2becac909..46e6e1675 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -5413,4 +5413,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory index 76d6ebfbc..cabf917eb 100644 --- a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory +++ b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory @@ -1,3 +1,4 @@ org.apache.fop.render.afp.extensions.AFPExtensionHandlerFactory +org.apache.fop.render.pdf.extensions.PDFExtensionHandlerFactory org.apache.fop.render.ps.extensions.PSExtensionHandlerFactory org.apache.fop.fo.extensions.xmp.XMPContentHandlerFactory diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java b/src/java/org/apache/fop/fo/extensions/ExtensionObj.java deleted file mode 100644 index c35dcfc73..000000000 --- a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java +++ /dev/null @@ -1,65 +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.fo.extensions; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.fo.FONode; -import org.apache.fop.fo.FObj; -import org.apache.fop.fo.PropertyList; - -/** - * Base class for pdf bookmark extension objects. - */ -public abstract class ExtensionObj extends FObj { - - /** - * Create a new extension object. - * - * @param parent the parent formatting object - */ - public ExtensionObj(FONode parent) { - super(parent); - } - - /** - * {@inheritDoc} - */ - public void processNode(String elementName, Locator locator, - Attributes attlist, PropertyList pList) - throws FOPException { - } - - /** - * Create a default property list for this element. - * @param parent the parent property list - * @param foEventHandler an event handler - * @return property list - * @throws FOPException in case of exception - */ - protected PropertyList createPropertyList(PropertyList parent, - FOEventHandler foEventHandler) throws FOPException { - return null; - } -} - diff --git a/src/java/org/apache/fop/pdf/PDFNumber.java b/src/java/org/apache/fop/pdf/PDFNumber.java index 1c31f8e9d..754194886 100644 --- a/src/java/org/apache/fop/pdf/PDFNumber.java +++ b/src/java/org/apache/fop/pdf/PDFNumber.java @@ -29,6 +29,14 @@ public class PDFNumber extends PDFObject { private Number number; + public PDFNumber() { + this.number = Integer.valueOf(0); + } + + public PDFNumber(Number number) { + this.number = number; + } + /** * Returns the number. * @return the number diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java index fcf8554a6..0c3305797 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java @@ -54,10 +54,10 @@ public abstract class AbstractXMLWritingIFDocumentHandler extends AbstractIFDocu if (result instanceof SAXResult) { SAXResult saxResult = (SAXResult)result; this.handler = new GenerationHelperContentHandler( - saxResult.getHandler(), getMainNamespace()); + saxResult.getHandler(), getMainNamespace(), getContext()); } else { this.handler = new GenerationHelperContentHandler( - createContentHandler(result), getMainNamespace()); + createContentHandler(result), getMainNamespace(), getContext()); } } diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java index fda0cff3b..7464e26e0 100644 --- a/src/java/org/apache/fop/render/intermediate/IFContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFContext.java @@ -55,6 +55,8 @@ public class IFContext { private boolean hyphenated; + private int pageIndex = -1; + /** * Main constructor. * @param ua the user agent @@ -216,4 +218,20 @@ public class IFContext { return hyphenated; } + /** + * Record current page index. + * @param pageIndex a zero based page index or -1 (no page) + */ + public void setPageIndex(int pageIndex) { + this.pageIndex = pageIndex; + } + + /** + * Obtain current page index. + * @return a zero based page index or -1 (no page) + */ + public int getPageIndex() { + return this.pageIndex; + } + } diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 9e6aae700..30ceda108 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -571,6 +571,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { Dimension dim = new Dimension(viewArea.width, viewArea.height); establishForeignAttributes(page.getForeignAttributes()); + documentHandler.getContext().setPageIndex(page.getPageIndex()); documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(), page.getSimplePageMasterName(), dim); resetForeignAttributes(); @@ -598,6 +599,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { establishForeignAttributes(page.getForeignAttributes()); documentHandler.endPage(); + documentHandler.getContext().setPageIndex(-1); resetForeignAttributes(); } catch (IFException e) { handleIFException(e); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 62e4cc67d..1ffd42863 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -303,6 +303,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler { addAttribute(atts, "width", Integer.toString(size.width)); addAttribute(atts, "height", Integer.toString(size.height)); addForeignAttributes(atts); + getContext().setPageIndex(index); handler.startElement(EL_PAGE, atts); } catch (SAXException e) { throw new IFException("SAX error in startPage()", e); @@ -379,6 +380,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler { public void endPage() throws IFException { try { handler.endElement(EL_PAGE); + getContext().setPageIndex(-1); } catch (SAXException e) { throw new IFException("SAX error in endPage()", e); } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index dd9320571..0a3d34ef4 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -50,7 +50,8 @@ import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFPainter; import org.apache.fop.render.pdf.PDFRendererConfig.PDFRendererConfigParser; -import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment; +import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment; +import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment; /** * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF. @@ -296,17 +297,22 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { } else if (extension instanceof Metadata) { XMPMetadata wrapper = new XMPMetadata(((Metadata) extension)); pdfUtil.renderXMPMetadata(wrapper); - } else if (extension instanceof PDFEmbeddedFileExtensionAttachment) { - PDFEmbeddedFileExtensionAttachment embeddedFile - = (PDFEmbeddedFileExtensionAttachment)extension; + } else if (extension instanceof PDFEmbeddedFileAttachment) { + PDFEmbeddedFileAttachment embeddedFile + = (PDFEmbeddedFileAttachment)extension; try { pdfUtil.addEmbeddedFile(embeddedFile); } catch (IOException ioe) { throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe); } - } else { + } else if (extension instanceof PDFDictionaryAttachment) { + PDFDictionaryAttachment dictionaryExtension = (PDFDictionaryAttachment) extension; + pdfUtil.renderDictionaryExtension(dictionaryExtension, currentPage); + } else if (extension != null) { log.debug("Don't know how to handle extension object. Ignoring: " + extension + " (" + extension.getClass().getName() + ")"); + } else { + log.debug("Ignoring null extension object."); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index c3b46794d..962cd7847 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -56,15 +56,23 @@ import org.apache.fop.pdf.PDFICCBasedColorSpace; import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFInfo; import org.apache.fop.pdf.PDFMetadata; +import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFNames; +import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFOutputIntent; +import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFPageLabels; import org.apache.fop.pdf.PDFReference; import org.apache.fop.pdf.PDFText; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; import org.apache.fop.pdf.VersionController; -import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment; +import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment; +import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryExtension; +import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryType; +import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension; +import org.apache.fop.render.pdf.extensions.PDFDictionaryType; +import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment; import static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS; import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT; @@ -250,6 +258,46 @@ class PDFRenderingUtil { } } + public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) { + PDFDictionaryExtension extension = attachment.getExtension(); + if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { + augmentDictionary(pdfDoc.getRoot(), extension); + } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { + if (extension.matchesPageNumber(currentPage.getPageIndex() + 1)) { + augmentDictionary(currentPage, extension); + } + } else { + throw new IllegalStateException(); + } + } + + private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) { + for (PDFDictionaryEntryExtension entry : extension.getEntries()) { + if (entry instanceof PDFDictionaryExtension) { + dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + } else { + augmentDictionary(dictionary, entry); + } + } + return dictionary; + } + + private void augmentDictionary(PDFDictionary dictionary, PDFDictionaryEntryExtension entry) { + PDFDictionaryEntryType type = entry.getType(); + String key = entry.getKey(); + if (type == PDFDictionaryEntryType.Boolean) { + dictionary.put(key, entry.getValueAsBoolean()); + } else if (type == PDFDictionaryEntryType.Name) { + dictionary.put(key, new PDFName(entry.getValueAsString())); + } else if (type == PDFDictionaryEntryType.Number) { + dictionary.put(key, new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFDictionaryEntryType.String) { + dictionary.put(key, entry.getValueAsString()); + } else { + throw new IllegalStateException(); + } + } + public PDFDocument setupPDFDocument(OutputStream out) throws IOException { if (this.pdfDoc != null) { throw new IllegalStateException("PDFDocument already set up"); @@ -315,7 +363,7 @@ class PDFRenderingUtil { * @param embeddedFile the object representing the embedded file to be added * @throws IOException if an I/O error occurs */ - public void addEmbeddedFile(PDFEmbeddedFileExtensionAttachment embeddedFile) + public void addEmbeddedFile(PDFEmbeddedFileAttachment embeddedFile) throws IOException { this.pdfDoc.getProfile().verifyEmbeddedFilesAllowed(); PDFNames names = this.pdfDoc.getRoot().getNames(); diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java new file mode 100644 index 000000000..9de7e95da --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Base class for the PDF dictionary related extension elements. + */ +public abstract class AbstractPDFDictionaryElement extends AbstractPDFExtensionElement { + + public static final String ATT_KEY = PDFDictionaryEntryExtension.PROPERTY_KEY; + + /** + * Default constructor + * + * @param parent parent of this node + * @see org.apache.fop.fo.FONode#FONode(FONode) + */ + public AbstractPDFDictionaryElement(FONode parent) { + super(parent); + } +} + diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java index 2b50112c0..b66fe4fad 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java @@ -19,10 +19,11 @@ package org.apache.fop.render.pdf.extensions; -// FOP import org.apache.fop.fo.FONode; import org.apache.fop.fo.extensions.ExtensionAttachment; +// CSOFF: LineLengthCheck + /** * Base class for the PDF-specific extension elements. */ @@ -67,7 +68,9 @@ public abstract class AbstractPDFExtensionElement extends FONode { * Instantiates extension attachment object. * @return extension attachment */ - protected abstract ExtensionAttachment instantiateExtensionAttachment(); + protected ExtensionAttachment instantiateExtensionAttachment() { + return null; + } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java new file mode 100644 index 000000000..528268c30 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.util.GenerationHelperContentHandler; + +// CSOFF: LineLengthCheck + +public class PDFDictionaryAttachment extends PDFExtensionAttachment { + + private static final long serialVersionUID = -5576832955238384505L; + + private PDFDictionaryExtension extension; + + public PDFDictionaryAttachment(PDFDictionaryExtension extension) { + this.extension = extension; + } + + public PDFDictionaryExtension getExtension() { + return extension; + } + + @Override + public void toSAX(ContentHandler handler) throws SAXException { + PDFDictionaryType dictionaryType = extension.getDictionaryType(); + int pageNumber = 0; + if (dictionaryType == PDFDictionaryType.Page) { + if (handler instanceof GenerationHelperContentHandler) { + Object context = ((GenerationHelperContentHandler) handler).getContentHandlerContext(); + if (context instanceof IFContext) { + int pageIndex = ((IFContext) context).getPageIndex(); + if ((pageIndex >= 0) && extension.matchesPageNumber(pageIndex + 1)) { + pageNumber = pageIndex + 1; + } else { + pageNumber = -1; + } + } + } + } + if (pageNumber >= 0) { + toSAX(handler, extension); + } + } + + private void toSAX(ContentHandler handler, PDFDictionaryExtension dictionary) throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + String ln = dictionary.getElementName(); + String qn = PREFIX + ":" + ln; + attributes = extractIFAttributes(attributes, dictionary); + handler.startElement(CATEGORY, ln, qn, attributes); + for (PDFDictionaryEntryExtension entry : dictionary.getEntries()) { + toSAX(handler, entry); + } + handler.endElement(CATEGORY, ln, qn); + } + + private void toSAX(ContentHandler handler, PDFDictionaryEntryExtension entry) throws SAXException { + if (entry instanceof PDFDictionaryExtension) { + toSAX(handler, (PDFDictionaryExtension) entry); + } else { + AttributesImpl attributes = new AttributesImpl(); + String ln = entry.getElementName(); + String qn = PREFIX + ":" + ln; + attributes = extractIFAttributes(attributes, entry); + handler.startElement(CATEGORY, ln, qn, attributes); + char[] characters = entry.getValueAsXMLEscapedString().toCharArray(); + if (characters.length > 0) { + handler.characters(characters, 0, characters.length); + } + handler.endElement(CATEGORY, ln, qn); + } + } + + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) { + PDFDictionaryType type = dictionary.getDictionaryType(); + if (type == PDFDictionaryType.Catalog) { + // no specific attriburtes + } else if (type == PDFDictionaryType.Page) { + String pageNumbersName = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + String pageNumbers = dictionary.getProperty(pageNumbersName); + if (pageNumbers != null) { + attributes.addAttribute(null, pageNumbersName, pageNumbersName, "CDATA", pageNumbers); + } + } else if (type == PDFDictionaryType.Dictionary) { + String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + String key = dictionary.getKey(); + if (key != null) { + attributes.addAttribute(null, keyName, keyName, "CDATA", key); + } + } + return attributes; + } + + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryEntryExtension entry) { + String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + String key = entry.getKey(); + if (key != null) { + attributes.addAttribute(null, keyName, keyName, "CDATA", key); + } + return attributes; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java new file mode 100644 index 000000000..5a60f20ca --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.extensions.ExtensionAttachment; + +// CSOFF: LineLengthCheck + +/** + * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type + * of dictionary is established at construction type. + */ +public class PDFDictionaryElement extends AbstractPDFDictionaryElement { + + public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + + private PDFDictionaryExtension extension; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFDictionaryElement(FONode parent, PDFDictionaryType type) { + super(parent); + this.extension = new PDFDictionaryExtension(type); + } + + public PDFDictionaryExtension getExtension() { + return extension; + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { + // no specific properties + } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { + String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + extension.setProperty(ATT_PAGE_NUMBERS, pageNumbers); + } + } else if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { + String key = attlist.getValue(ATT_KEY); + if (key == null) { + missingPropertyError(ATT_KEY); + } else if (key.isEmpty()) { + invalidPropertyValueError(ATT_KEY, key, null); + } else { + extension.setKey(key); + } + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + String localName = getLocalName(); + if (localName.equals("catalog")) { + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } else if (localName.equals("page")) { + if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM"); + } + } else if (localName.equals("dictionary")) { + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); + } + } else { + throw new IllegalStateException("unknown name: " + localName); + } + } + + @Override + protected void addChildNode(FONode child) throws FOPException { + if (child instanceof PDFDictionaryElement) { + PDFDictionaryExtension extension = ((PDFDictionaryElement) child).getExtension(); + if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { + this.extension.addEntry(extension); + } + } else if (child instanceof PDFDictionaryEntryElement) { + PDFDictionaryEntryExtension extension = ((PDFDictionaryEntryElement) child).getExtension(); + this.extension.addEntry(extension); + } + } + + @Override + public void endOfNode() throws FOPException { + super.endOfNode(); + } + + @Override + public String getLocalName() { + return extension.getDictionaryType().elementName(); + } + + @Override + protected ExtensionAttachment instantiateExtensionAttachment() { + return new PDFDictionaryAttachment(extension); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java new file mode 100644 index 000000000..d8ef1f04e --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for dictionary entries: pdf:{boolean,name,number,string}. The specific type + * of entry is established at construction type. + */ +public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement { + + private PDFDictionaryEntryExtension extension; + private StringBuffer characters; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFDictionaryEntryElement(FONode parent, PDFDictionaryEntryType type) { + super(parent); + this.extension = new PDFDictionaryEntryExtension(type); + } + + public PDFDictionaryEntryExtension getExtension() { + return extension; + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + String key = attlist.getValue("key"); + if (key == null) { + missingPropertyError("key"); + } else if (key.isEmpty()) { + invalidPropertyValueError("key", key, null); + } else { + extension.setKey(key); + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); + } + } + + @Override + protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException { + if (characters == null) { + characters = new StringBuffer((length < 16) ? 16 : length); + } + characters.append(data, start, length); + } + + @Override + public void endOfNode() throws FOPException { + String value = (characters != null) ? characters.toString() : ""; + if (extension.getType() == PDFDictionaryEntryType.Boolean) { + if (!value.equals("true") && !value.equals("false")) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() == PDFDictionaryEntryType.Name) { + if (value.isEmpty()) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() == PDFDictionaryEntryType.Number) { + try { + Double.valueOf(value); + } catch (NumberFormatException e) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() != PDFDictionaryEntryType.String) { + throw new IllegalStateException(); + } + extension.setValue(value); + super.endOfNode(); + } + + @Override + public String getLocalName() { + return extension.getType().elementName(); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java new file mode 100644 index 000000000..94d6b5dbf --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.util.XMLUtil; + +// CSOFF: LineLengthCheck + +public class PDFDictionaryEntryExtension { + + public static final String PROPERTY_KEY = "key"; + + private PDFDictionaryEntryType type; + private String key = ""; + private Object value; + + PDFDictionaryEntryExtension() { + } + + PDFDictionaryEntryExtension(PDFDictionaryEntryType type) { + this.type = type; + } + + public PDFDictionaryEntryType getType() { + return type; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public void setValue(String value) { + this.value = value; + } + + public Object getValue() { + return value; + } + + /** + * Obtain entry value as Boolean. + * @return entry value + */ + public Boolean getValueAsBoolean() { + if (value instanceof String) { + return Boolean.valueOf((String)value); + } else { + return false; + } + } + + /** + * Obtain entry value as Number. + * @return entry value + */ + public Number getValueAsNumber() { + if (value instanceof String) { + double d = Double.parseDouble((String) value); + if (Math.floor(d) == d) { + return Long.valueOf((long) d); + } else { + return Double.valueOf(d); + } + } else { + return Integer.valueOf(0); + } + } + + public String getValueAsString() { + if (value instanceof String) { + return (String) value; + } else { + return ""; + } + } + + public String getValueAsXMLEscapedString() { + return XMLUtil.escape(getValueAsString()); + } + + public void setValue(Object value) { + this.value = value; + } + + public String getElementName() { + return type.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java new file mode 100644 index 000000000..e4b25819a --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +/** + * Enumeration type for leaf PDF dictionary entry extension elements. + */ +public enum PDFDictionaryEntryType { + Boolean("boolean"), // boolean valued entry + Dictionary("dictionary"), // dictionary valued entry + Name("name"), // name valued entry + Number("number"), // number valued entry + String("string"); // string valued entry + + private String elementName; + PDFDictionaryEntryType(String elementName) { + this.elementName = elementName; + } + public String elementName() { + return elementName; + } + static PDFDictionaryEntryType valueOfElementName(String elementName) { + for (PDFDictionaryEntryType type : values()) { + if (type.elementName.equals(elementName)) { + return type; + } + } + throw new IllegalArgumentException(); + } + static boolean hasValueOfElementName(String elementName) { + try { + return valueOfElementName(elementName) != null; + } catch (Exception e) { + return false; + } + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java new file mode 100644 index 000000000..2b851e0bf --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import java.util.List; +import java.util.Map; + +// CSOFF: LineLengthCheck + +public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { + + public static final String PROPERTY_PAGE_NUMBERS = "page-numbers"; + + private static final long serialVersionUID = -1L; + + private PDFDictionaryType dictionaryType; + private Map properties; + private List entries; + + PDFDictionaryExtension() { + } + + PDFDictionaryExtension(PDFDictionaryType dictionaryType) { + super(PDFDictionaryEntryType.Dictionary); + this.dictionaryType = dictionaryType; + this.properties = new java.util.HashMap(); + this.entries = new java.util.ArrayList(); + } + + public PDFDictionaryType getDictionaryType() { + return dictionaryType; + } + + public void setProperty(String name, String value) { + properties.put(name, value); + } + + public String getProperty(String name) { + return properties.get(name); + } + + public void addEntry(PDFDictionaryEntryExtension entry) { + entries.add(entry); + } + + public List getEntries() { + return entries; + } + + public PDFDictionaryEntryExtension getLastEntry() { + if (entries.size() > 0) { + return entries.get(entries.size() - 1); + } else { + return null; + } + } + + /** + * Determine if page dictionary and page number matches. + * @param pageNumber page number, where first page number is 1 + * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property + */ + public boolean matchesPageNumber(int pageNumber) { + if (dictionaryType != PDFDictionaryType.Page) { + return false; + } + String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); + if ((pageNumbers == null) || pageNumbers.isEmpty()) { + return false; + } else if (pageNumbers.equals("*")) { + return true; + } else { + for (String interval : pageNumbers.split("\\s*,\\s*")) { + String[] components = interval.split("\\s*-\\s*"); + if (components.length < 1) { + continue; + } else { + try { + int start = Integer.parseInt(components[0]); + int end = 0; + if (components.length > 1) { + if (!components[1].equals("LAST")) { + end = Integer.parseInt(components[1]); + } + } + if ((end == 0) && (pageNumber == start)) { + return true; + } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) { + return true; + } else { + continue; + } + } catch (NumberFormatException e) { + continue; + } + } + } + } + return false; + } + + @Override + public String getElementName() { + return dictionaryType.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java new file mode 100644 index 000000000..edd95160a --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +/** + * Enumeration type for PDF dictionary extension elements. + */ +public enum PDFDictionaryType { + Dictionary("dictionary"), // generic (nested) dictionary element + Catalog("catalog"), // catalog dictionary element + Page("page"); // page dictionary element + + private String elementName; + PDFDictionaryType(String elementName) { + this.elementName = elementName; + } + public String elementName() { + return elementName; + } + static PDFDictionaryType valueOfElementName(String elementName) { + for (PDFDictionaryType type : values()) { + if (type.elementName.equals(elementName)) { + return type; + } + } + throw new IllegalArgumentException(); + } + static boolean hasValueOfElementName(String elementName) { + try { + return valueOfElementName(elementName) != null; + } catch (Exception e) { + return false; + } + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java index c70ed3635..3e063e24b 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java @@ -22,6 +22,8 @@ package org.apache.fop.render.pdf.extensions; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.FONode; +// CSOFF: LineLengthCheck + /** * This class provides the element mapping for the PDF-specific extensions. */ @@ -39,13 +41,43 @@ public class PDFElementMapping extends ElementMapping { protected void initialize() { if (foObjs == null) { foObjs = new java.util.HashMap(); - foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileMaker()); + // pdf:embedded-file + foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileElementMaker()); + // pdf:{catalog,page} et al. + for (PDFDictionaryType type : PDFDictionaryType.values()) { + foObjs.put(type.elementName(), new PDFDictionaryElementMaker(type)); + } + for (PDFDictionaryEntryType type : PDFDictionaryEntryType.values()) { + if (type != PDFDictionaryEntryType.Dictionary) { + foObjs.put(type.elementName(), new PDFDictionaryEntryElementMaker(type)); + } + } } } - static class PDFEmbeddedFileMaker extends ElementMapping.Maker { + static class PDFEmbeddedFileElementMaker extends ElementMapping.Maker { public FONode make(FONode parent) { return new PDFEmbeddedFileElement(parent); } } + + static class PDFDictionaryElementMaker extends ElementMapping.Maker { + private PDFDictionaryType dictionaryType; + PDFDictionaryElementMaker(PDFDictionaryType dictionaryType) { + this.dictionaryType = dictionaryType; + } + public FONode make(FONode parent) { + return new PDFDictionaryElement(parent, dictionaryType); + } + } + + static class PDFDictionaryEntryElementMaker extends ElementMapping.Maker { + private PDFDictionaryEntryType entryType; + PDFDictionaryEntryElementMaker(PDFDictionaryEntryType entryType) { + this.entryType = entryType; + } + public FONode make(FONode parent) { + return new PDFDictionaryEntryElement(parent, entryType); + } + } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java new file mode 100644 index 000000000..a62b35a4b --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * This is the pass-through value object for the PDF extension. + */ +public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment { + + private static final long serialVersionUID = -1L; + + /** element name */ + protected static final String ELEMENT = "embedded-file"; + + /** name of file to be embedded */ + private static final String ATT_NAME = "filename"; + + /** source of file to be embedded (URI) */ + private static final String ATT_SRC = "src"; + + /** a description of the file to be embedded */ + private static final String ATT_DESC = "desc"; + + /** filename attribute */ + private String filename = null; + + /** description attribute (optional) */ + private String desc = null; + + /** source name attribute */ + private String src = null; + + /** + * No-argument contructor. + */ + public PDFEmbeddedFileAttachment() { + super(); + } + + /** + * Default constructor. + * @param filename the name of the file + * @param src the location of the file + * @param desc the description of the file + */ + public PDFEmbeddedFileAttachment(String filename, String src, String desc) { + super(); + this.filename = filename; + this.src = src; + this.desc = desc; + } + + /** + * Returns the file name. + * @return the file name + */ + public String getFilename() { + return filename; + } + + /** + * Sets the file name. + * @param name The file name to set. + */ + public void setFilename(String name) { + this.filename = name; + } + + /** + * Returns the file description. + * @return the description + */ + public String getDesc() { + return desc; + } + + /** + * Sets the description of the file. + * @param desc the description to set + */ + public void setDesc(String desc) { + this.desc = desc; + } + + /** + * Returns the source URI of the file. + * @return the source URI + */ + public String getSrc() { + return src; + } + + /** + * Sets the source URI of the file. + * @param src the source URI + */ + public void setSrc(String src) { + this.src = src; + } + + /** {@inheritDoc} */ + public String getCategory() { + return CATEGORY; + } + + /** {@inheritDoc} */ + public String toString() { + return "PDFEmbeddedFile(name=" + getFilename() + ", " + getSrc() + ")"; + } + + /** + * @return the element name + */ + protected String getElement() { + return ELEMENT; + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + if (filename != null && filename.length() > 0) { + atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", filename); + } + if (src != null && src.length() > 0) { + atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", src); + } + if (desc != null && desc.length() > 0) { + atts.addAttribute(null, ATT_DESC, ATT_DESC, "CDATA", desc); + } + String element = getElement(); + handler.startElement(CATEGORY, element, element, atts); + handler.endElement(CATEGORY, element, element); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java index 36d7c18d2..3fef5521e 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java @@ -30,7 +30,6 @@ import org.apache.fop.datatypes.URISpecification; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; -import org.apache.fop.fo.extensions.ExtensionAttachment; /** * Extension element for pdf:embedded-file. @@ -44,11 +43,11 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement { * Main constructor * @param parent parent FO node */ - protected PDFEmbeddedFileElement(FONode parent) { + PDFEmbeddedFileElement(FONode parent) { super(parent); } - /** {@inheritDoc} */ + @Override public void startOfNode() throws FOPException { super.startOfNode(); if (parent.getNameId() != Constants.FO_DECLARATIONS) { @@ -57,12 +56,12 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement { } } - /** {@inheritDoc} */ + @Override public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { - PDFEmbeddedFileExtensionAttachment embeddedFile - = (PDFEmbeddedFileExtensionAttachment)getExtensionAttachment(); + PDFEmbeddedFileAttachment embeddedFile + = (PDFEmbeddedFileAttachment)getExtensionAttachment(); String desc = attlist.getValue("description"); if (desc != null && desc.length() > 0) { embeddedFile.setDesc(desc); @@ -94,13 +93,8 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement { embeddedFile.setFilename(filename); } - /** {@inheritDoc} */ + @Override public String getLocalName() { return ELEMENT; } - - /** {@inheritDoc} */ - protected ExtensionAttachment instantiateExtensionAttachment() { - return new PDFEmbeddedFileExtensionAttachment(); - } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java deleted file mode 100644 index 5f3f16dbb..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.AttributesImpl; - -/** - * This is the pass-through value object for the PDF extension. - */ -public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment { - - /** element name */ - protected static final String ELEMENT = "embedded-file"; - - /** name of file to be embedded */ - private static final String ATT_NAME = "filename"; - - /** source of file to be embedded (URI) */ - private static final String ATT_SRC = "src"; - - /** a description of the file to be embedded */ - private static final String ATT_DESC = "desc"; - - /** filename attribute */ - private String filename = null; - - /** description attribute (optional) */ - private String desc = null; - - /** source name attribute */ - private String src = null; - - /** - * No-argument contructor. - */ - public PDFEmbeddedFileExtensionAttachment() { - super(); - } - - /** - * Default constructor. - * @param filename the name of the file - * @param src the location of the file - * @param desc the description of the file - */ - public PDFEmbeddedFileExtensionAttachment(String filename, String src, String desc) { - super(); - this.filename = filename; - this.src = src; - this.desc = desc; - } - - /** - * Returns the file name. - * @return the file name - */ - public String getFilename() { - return filename; - } - - /** - * Sets the file name. - * @param name The file name to set. - */ - public void setFilename(String name) { - this.filename = name; - } - - /** - * Returns the file description. - * @return the description - */ - public String getDesc() { - return desc; - } - - /** - * Sets the description of the file. - * @param desc the description to set - */ - public void setDesc(String desc) { - this.desc = desc; - } - - /** - * Returns the source URI of the file. - * @return the source URI - */ - public String getSrc() { - return src; - } - - /** - * Sets the source URI of the file. - * @param src the source URI - */ - public void setSrc(String src) { - this.src = src; - } - - /** {@inheritDoc} */ - public String getCategory() { - return CATEGORY; - } - - /** {@inheritDoc} */ - public String toString() { - return "PDFEmbeddedFile(name=" + getFilename() + ", " + getSrc() + ")"; - } - - /** - * @return the element name - */ - protected String getElement() { - return ELEMENT; - } - - /** {@inheritDoc} */ - public void toSAX(ContentHandler handler) throws SAXException { - AttributesImpl atts = new AttributesImpl(); - if (filename != null && filename.length() > 0) { - atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", filename); - } - if (src != null && src.length() > 0) { - atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", src); - } - if (desc != null && desc.length() > 0) { - atts.addAttribute(null, ATT_DESC, ATT_DESC, "CDATA", desc); - } - String element = getElement(); - handler.startElement(CATEGORY, element, element, atts); - handler.endElement(CATEGORY, element, element); - } - -} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java index a4f5b47b7..58664faa0 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java @@ -23,43 +23,28 @@ import org.apache.xmlgraphics.util.XMLizable; import org.apache.fop.fo.extensions.ExtensionAttachment; -/** - * This is the pass-through value object for the PDF extension. - */ +// CSOFF: LineLengthCheck + public abstract class PDFExtensionAttachment implements ExtensionAttachment, XMLizable { /** The category URI for this extension attachment. */ public static final String CATEGORY = "apache:fop:extensions:pdf"; + /** The prefix to use with qualified names for this extension attachment. */ + public static final String PREFIX = "pdf"; + /** * Default constructor. */ public PDFExtensionAttachment() { - //nop - } - - /** - * @return the category URI - * @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory() - */ - public String getCategory() { - return CATEGORY; } - /** @return type name */ - public String getType() { - String className = getClass().getName(); - return className.substring(className.lastIndexOf('.') + 3); + public String getPrefix() { + return PREFIX; } - /** - * @return a string representation of this object - * @see java.lang.Object#toString() - */ - public String toString() { - return getType(); + @Override + public String getCategory() { + return CATEGORY; } - - /** @return element */ - protected abstract String getElement(); } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java index e7e863a23..2741e64f6 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java @@ -19,6 +19,8 @@ package org.apache.fop.render.pdf.extensions; +import java.util.Stack; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -30,68 +32,127 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; +// CSOFF: LineLengthCheck + /** * ContentHandler (parser) for restoring PDF extension objects from XML. */ -public class PDFExtensionHandler extends DefaultHandler - implements ContentHandlerFactory.ObjectSource { +public class PDFExtensionHandler extends DefaultHandler implements ContentHandlerFactory.ObjectSource { /** Logger instance */ protected static final Log log = LogFactory.getLog(PDFExtensionHandler.class); - private Attributes lastAttributes; - private PDFExtensionAttachment returnedObject; private ObjectBuiltListener listener; - /** {@inheritDoc} */ - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - boolean handled = false; + // PDFEmbeddedFileAttachment related state + private Attributes lastAttributes; + + // PDFDictionaryAttachment related + private Stack dictionaries = new Stack(); + private boolean captureContent; + private StringBuffer characters; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (PDFExtensionAttachment.CATEGORY.equals(uri)) { - lastAttributes = new AttributesImpl(attributes); - handled = false; - if (localName.equals(PDFEmbeddedFileExtensionAttachment.ELEMENT)) { - //handled in endElement - handled = true; + if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) { + lastAttributes = new AttributesImpl(attributes); + } else if (PDFDictionaryType.hasValueOfElementName(localName)) { + PDFDictionaryExtension dictionary = new PDFDictionaryExtension(PDFDictionaryType.valueOfElementName(localName)); + String key = attributes.getValue(PDFDictionaryEntryExtension.PROPERTY_KEY); + if (key != null) { + dictionary.setKey(key); + } + if (dictionary.getDictionaryType() == PDFDictionaryType.Page) { + String pageNumbers = attributes.getValue(PDFDictionaryElement.ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + dictionary.setProperty(PDFDictionaryElement.ATT_PAGE_NUMBERS, pageNumbers); + } + } + dictionaries.push(dictionary); + } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { + PDFDictionaryEntryExtension entry = new PDFDictionaryEntryExtension(PDFDictionaryEntryType.valueOfElementName(localName)); + String key = attributes.getValue(PDFDictionaryEntryElement.ATT_KEY); + if (key != null) { + entry.setKey(key); + } + if (!dictionaries.empty()) { + PDFDictionaryExtension dictionary = dictionaries.peek(); + dictionary.addEntry(entry); + captureContent = true; + } + } else { + throw new SAXException("Unhandled element " + localName + " in namespace: " + uri); } + } else { + log.warn("Unhandled element " + localName + " in namespace: " + uri); } - if (!handled) { - if (PDFExtensionAttachment.CATEGORY.equals(uri)) { - throw new SAXException("Unhandled element " + localName - + " in namespace: " + uri); - } else { - log.warn("Unhandled element " + localName - + " in namespace: " + uri); + } + + @Override + public void characters(char[] data, int start, int length) throws SAXException { + if (captureContent) { + if (characters == null) { + characters = new StringBuffer((length < 16) ? 16 : length); } + characters.append(data, start, length); } } - /** {@inheritDoc} */ + @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (PDFExtensionAttachment.CATEGORY.equals(uri)) { - if (PDFEmbeddedFileExtensionAttachment.ELEMENT.equals(localName)) { + if (PDFEmbeddedFileAttachment.ELEMENT.equals(localName)) { String name = lastAttributes.getValue("name"); String src = lastAttributes.getValue("src"); String desc = lastAttributes.getValue("description"); - this.returnedObject = new PDFEmbeddedFileExtensionAttachment(name, src, desc); + this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc); + } else if (PDFDictionaryType.hasValueOfElementName(localName)) { + if (!dictionaries.empty()) { + PDFDictionaryExtension dictionary = dictionaries.pop(); + if ((dictionary.getDictionaryType() == PDFDictionaryType.Catalog) || (dictionary.getDictionaryType() == PDFDictionaryType.Page)) { + this.returnedObject = new PDFDictionaryAttachment(dictionary); + } else if (!dictionaries.empty()) { + PDFDictionaryExtension dictionaryOuter = dictionaries.peek(); + dictionaryOuter.addEntry(dictionary); + } + } else { + throw new SAXException(new IllegalStateException("no active dictionary")); + } + } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { + if (!dictionaries.empty()) { + PDFDictionaryExtension dictionary = dictionaries.peek(); + PDFDictionaryEntryExtension entry = dictionary.getLastEntry(); + if (entry != null) { + if (characters != null) { + entry.setValue(characters.toString()); + characters = null; + } + } else { + throw new SAXException(new IllegalStateException("no active entry")); + } + } else { + throw new SAXException(new IllegalStateException("no active dictionary")); + } } } + captureContent = false; } - /** {@inheritDoc} */ + @Override public void endDocument() throws SAXException { if (listener != null) { listener.notifyObjectBuilt(getObject()); } } - /** {@inheritDoc} */ + @Override public Object getObject() { return returnedObject; } - /** {@inheritDoc} */ + @Override public void setObjectBuiltListener(ObjectBuiltListener listener) { this.listener = listener; } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java new file mode 100644 index 000000000..6d83cd58f --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.ContentHandler; + +import org.apache.fop.util.ContentHandlerFactory; + +// CSOFF: LineLengthCheck + +/** + * Factory for the ContentHandler that handles serialized PDFExtensionAttachment instances. + */ +public class PDFExtensionHandlerFactory implements ContentHandlerFactory { + + private static final String[] NAMESPACES = new String[] {PDFExtensionAttachment.CATEGORY}; + + /** {@inheritDoc} */ + public String[] getSupportedNamespaces() { + return NAMESPACES; + } + + /** {@inheritDoc} */ + public ContentHandler createContentHandler() { + return new PDFExtensionHandler(); + } +} diff --git a/src/java/org/apache/fop/tools/fontlist/FontListMain.java b/src/java/org/apache/fop/tools/fontlist/FontListMain.java index 659f7ccac..e2f5a651e 100644 --- a/src/java/org/apache/fop/tools/fontlist/FontListMain.java +++ b/src/java/org/apache/fop/tools/fontlist/FontListMain.java @@ -117,7 +117,7 @@ public final class FontListMain { } try { GenerationHelperContentHandler helper = new GenerationHelperContentHandler( - handler, null); + handler, null, null); FontListSerializer serializer = new FontListSerializer(); serializer.generateSAX(fontFamilies, singleFamily, helper); } finally { diff --git a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java index 64fabbc8a..68970a7f2 100644 --- a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java +++ b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java @@ -35,6 +35,7 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler { private static final Attributes EMPTY_ATTS = new AttributesImpl(); private String mainNamespace; + private Object contentHandlerContext; /** * Main constructor. If the given handler also implements any of the EntityResolver, @@ -42,10 +43,12 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler { * @param handler the SAX content handler to delegate all calls to * @param mainNamespace the main namespace used for generated XML content when abbreviated * ContentHandler calls are used. + * @param contentHandlerContext additional content handler context state */ - public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace) { + public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace, Object contentHandlerContext) { super(handler); this.mainNamespace = mainNamespace; + this.contentHandlerContext = contentHandlerContext; } /** @@ -65,6 +68,14 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler { this.mainNamespace = namespaceURI; } + /** + * Returns the context object (may be null). + * @return the context object + */ + public Object getContentHandlerContext() { + return this.contentHandlerContext; + } + /** * Convenience method to generate a startElement SAX event. * @param localName the local name of the element diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java index 24c75922c..3f815e59e 100644 --- a/src/java/org/apache/fop/util/XMLUtil.java +++ b/src/java/org/apache/fop/util/XMLUtil.java @@ -300,4 +300,35 @@ public final class XMLUtil implements XMLConstants { } } + /** + * Escape '<', '>' and '&' using NCRs. + * @param unescaped string + * @return escaped string + */ + public static String escape(String unescaped) { + int needsEscape = 0; + for (int i = 0, n = unescaped.length(); i < n; ++i) { + char c = unescaped.charAt(i); + if ((c == '<') || (c == '>') || (c == '&')) { + ++needsEscape; + } + } + if (needsEscape > 0) { + StringBuffer sb = new StringBuffer(unescaped.length() + 6 * needsEscape); + for (int i = 0, n = unescaped.length(); i < n; ++i) { + char c = unescaped.charAt(i); + if ((c == '<') || (c == '>') || (c == '&')) { + sb.append("&#x"); + sb.append(Integer.toString(c, 16)); + sb.append(';'); + } else { + sb.append(c); + } + } + return sb.toString(); + } else { + return unescaped; + } + } + } diff --git a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java index 12dd948e5..f7bfbb38e 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java @@ -229,7 +229,7 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler { } private GenerationHelperContentHandler decorate(ContentHandler contentHandler) { - return new GenerationHelperContentHandler(contentHandler, getMainNamespace()); + return new GenerationHelperContentHandler(contentHandler, getMainNamespace(), getContext()); } private void closeCurrentStream() { diff --git a/status.xml b/status.xml index d17168936..5f29deefb 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Enable support for PDF page transitions. + Fix and improve the handling of temporary files using the new URI resource resolvers diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml new file mode 100644 index 000000000..ecbf1bafd --- /dev/null +++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml @@ -0,0 +1,90 @@ + + + + + +

+ This test checks the PDF dictionary extensions. +

+
+ + + + + + + + + 5 + + + + Trans + 1 + Glitter + 0 + + + + + Trans + 1 + Push + 180 + + + + + Trans + 1 + Cover + 270 + + + + + + + Bar + + + + + SLIDE 1 + SLIDE 2 + SLIDE 3 + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From e39326ac56573b9cc97830e365c7dbf403172f41 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Mon, 16 Sep 2013 15:35:23 +0000 Subject: FOP-2298: Java 5 doesn't support String.isEmpty git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1523706 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java | 2 +- .../apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java | 4 ++-- .../org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java index 5a60f20ca..0920f3a78 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java @@ -66,7 +66,7 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { String key = attlist.getValue(ATT_KEY); if (key == null) { missingPropertyError(ATT_KEY); - } else if (key.isEmpty()) { + } else if (key.length() == 0) { invalidPropertyValueError(ATT_KEY, key, null); } else { extension.setKey(key); diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java index d8ef1f04e..bcdb90c2c 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java @@ -55,7 +55,7 @@ public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement { String key = attlist.getValue("key"); if (key == null) { missingPropertyError("key"); - } else if (key.isEmpty()) { + } else if (key.length() == 0) { invalidPropertyValueError("key", key, null); } else { extension.setKey(key); @@ -86,7 +86,7 @@ public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement { invalidPropertyValueError("", value, null); } } else if (extension.getType() == PDFDictionaryEntryType.Name) { - if (value.isEmpty()) { + if (value.length() == 0) { invalidPropertyValueError("", value, null); } } else if (extension.getType() == PDFDictionaryEntryType.Number) { diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java index 2b851e0bf..d4b11cdb4 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java @@ -82,7 +82,7 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { return false; } String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); - if ((pageNumbers == null) || pageNumbers.isEmpty()) { + if ((pageNumbers == null) || (pageNumbers.length() == 0)) { return false; } else if (pageNumbers.equals("*")) { return true; -- cgit v1.2.3 From 75d4ce44d553ae0a84cb4b4787e3e25be4a9fdd7 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Mon, 16 Sep 2013 15:42:35 +0000 Subject: FOP-2298: Java 5 is more strict than Java 6 about use of @Override on implementation of interface methods. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1523709 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java | 1 - src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java | 2 -- 2 files changed, 3 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java index 58664faa0..9ac858309 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java @@ -43,7 +43,6 @@ public abstract class PDFExtensionAttachment implements ExtensionAttachment, XML return PREFIX; } - @Override public String getCategory() { return CATEGORY; } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java index 2741e64f6..f14f1e7d6 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java @@ -147,12 +147,10 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle } } - @Override public Object getObject() { return returnedObject; } - @Override public void setObjectBuiltListener(ObjectBuiltListener listener) { this.listener = listener; } -- cgit v1.2.3 From aea90d5225059a0264ef68fdee6d49d517fb6b68 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Tue, 17 Sep 2013 20:28:10 +0000 Subject: FOP-2298: One last @Override to remove to satisfy broken Java 5 support. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1524211 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java | 1 - 1 file changed, 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java index 528268c30..19e5ce07a 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java @@ -42,7 +42,6 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { return extension; } - @Override public void toSAX(ContentHandler handler) throws SAXException { PDFDictionaryType dictionaryType = extension.getDictionaryType(); int pageNumber = 0; -- cgit v1.2.3 From bd40eedf996567982667e5987f64be54df417f1e Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 25 Sep 2013 23:09:55 +0000 Subject: FOP-2107: org.apache.batik.bridge.BridgeException: Unable to make sense of URL for connection; patch suggested by Matthias Reischenbacher and Robert Meyer comments. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1526329 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/apps/FOUserAgent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index 68064343e..8b9e079ab 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -408,7 +408,7 @@ public class FOUserAgent { try { // Have to do this so we can resolve data URIs StreamSource src = new StreamSource(resourceResolver.getResource(uri)); - src.setSystemId(uri); + src.setSystemId(new File(uri).toURI().toURL().toExternalForm()); return src; } catch (URISyntaxException use) { return null; -- cgit v1.2.3 From 54aa3369126225b7152d8692796babbc7f79135b Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 8 Oct 2013 11:29:05 +0000 Subject: FOP-2106: Footnote put on earlier page than the one that contains the footnote call Patch by Alexey Neyman, committed with minor modifications git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1530232 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/PageBreakingAlgorithm.java | 78 +++++++++++++--------- .../standard-testcases/footnote_jira2106.xml | 52 +++++++++++++++ 2 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 test/layoutengine/standard-testcases/footnote_jira2106.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 59145dd72..a5084bac5 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -140,7 +140,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { */ protected class KnuthPageNode extends KnuthNode { - /** Additional length due to footnotes. */ + /** Additional length due to already inserted footnotes. */ + public int insertedFootnotes; + + /** Total length of the footnotes. */ public int totalFootnotes; /** Index of the last inserted footnote. */ @@ -152,7 +155,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { public KnuthPageNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, - int totalFootnotes, int footnoteListIndex, int footnoteElementIndex, + int insertedFootnotes, int totalFootnotes, + int footnoteListIndex, int footnoteElementIndex, double adjustRatio, int availableShrink, int availableStretch, int difference, double totalDemerits, KnuthNode previous) { super(position, line, fitness, @@ -160,6 +164,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { adjustRatio, availableShrink, availableStretch, difference, totalDemerits, previous); this.totalFootnotes = totalFootnotes; + this.insertedFootnotes = insertedFootnotes; this.footnoteListIndex = footnoteListIndex; this.footnoteElementIndex = footnoteElementIndex; } @@ -172,7 +177,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { */ protected class BestPageRecords extends BestRecords { - private int[] bestFootnotesLength = new int[4]; + private int[] bestInsertedFootnotesLength = new int[4]; + private int[] bestTotalFootnotesLength = new int[4]; private int[] bestFootnoteListIndex = new int[4]; private int[] bestFootnoteElementIndex = new int[4]; @@ -182,13 +188,18 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { super.addRecord(demerits, node, adjust, availableShrink, availableStretch, difference, fitness); - bestFootnotesLength[fitness] = insertedFootnotesLength; + bestInsertedFootnotesLength[fitness] = insertedFootnotesLength; + bestTotalFootnotesLength[fitness] = totalFootnotesLength; bestFootnoteListIndex[fitness] = footnoteListIndex; bestFootnoteElementIndex[fitness] = footnoteElementIndex; } - public int getFootnotesLength(int fitness) { - return bestFootnotesLength[fitness]; + public int getInsertedFootnotesLength(int fitness) { + return bestInsertedFootnotesLength[fitness]; + } + + public int getTotalFootnotesLength(int fitness) { + return bestTotalFootnotesLength[fitness]; } public int getFootnoteListIndex(int fitness) { @@ -287,7 +298,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { int difference, double totalDemerits, KnuthNode previous) { return new KnuthPageNode(position, line, fitness, totalWidth, totalStretch, totalShrink, - insertedFootnotesLength, footnoteListIndex, footnoteElementIndex, + insertedFootnotesLength, totalFootnotesLength, + footnoteListIndex, footnoteElementIndex, adjustRatio, availableShrink, availableStretch, difference, totalDemerits, previous); } @@ -298,7 +310,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { int totalWidth, int totalStretch, int totalShrink) { return new KnuthPageNode(position, line, fitness, totalWidth, totalStretch, totalShrink, - ((BestPageRecords) best).getFootnotesLength(fitness), + ((BestPageRecords) best).getInsertedFootnotesLength(fitness), + ((BestPageRecords) best).getTotalFootnotesLength(fitness), ((BestPageRecords) best).getFootnoteListIndex(fitness), ((BestPageRecords) best).getFootnoteElementIndex(fitness), best.getAdjust(fitness), best.getAvailableShrink(fitness), @@ -405,6 +418,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { resetFootnotes(((KnuthBlockBox) resetElement).getElementLists()); } } + assert restartingNode instanceof KnuthPageNode; + KnuthPageNode restartingPageNode = (KnuthPageNode) restartingNode; + footnoteElementIndex = restartingPageNode.footnoteElementIndex; + footnoteListIndex = restartingPageNode.footnoteListIndex; + totalFootnotesLength = restartingPageNode.totalFootnotes; + insertedFootnotesLength = restartingPageNode.insertedFootnotes; } return returnValue; } @@ -413,13 +432,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { for (int i = 0; i < elementLists.size(); i++) { ListUtil.removeLast(footnotesList); ListUtil.removeLast(lengthList); - - // update totalFootnotesLength - if (!lengthList.isEmpty()) { - totalFootnotesLength = ListUtil.getLast(lengthList); - } else { - totalFootnotesLength = 0; - } } // update footnotesPending; if (footnotesList.size() == 0) { @@ -502,7 +514,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } if (footnotesPending) { // compute the total length of the footnotes not yet inserted - int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes; + int allFootnotes = totalFootnotesLength - pageNode.insertedFootnotes; if (allFootnotes > 0) { // this page contains some footnote citations // add the footnote separator width @@ -511,7 +523,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // there is enough space to insert all footnotes: // add the whole allFootnotes length actualWidth += allFootnotes; - insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; + insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes; footnoteListIndex = footnotesList.size() - 1; footnoteElementIndex = getFootnoteList(footnoteListIndex).size() - 1; @@ -528,7 +540,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // this is the first feasible break; in this case it is allowed // to break and defer, if necessary, old and new footnotes actualWidth += footnoteSplit; - insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit; + insertedFootnotesLength = pageNode.insertedFootnotes + footnoteSplit; // footnoteListIndex has been set in getFootnoteSplit() // footnoteElementIndex has been set in getFootnoteSplit() } else { @@ -538,7 +550,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // that cannot be broken: // add the whole allFootnotes length, so this breakpoint will be discarded actualWidth += allFootnotes; - insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; + insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes; footnoteListIndex = footnotesList.size() - 1; footnoteElementIndex = getFootnoteList(footnoteListIndex).size() - 1; @@ -569,7 +581,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) { return (noBreakBetween(node.position, contentElementIndex) && deferredFootnotes(node.footnoteListIndex, - node.footnoteElementIndex, node.totalFootnotes)); + node.footnoteElementIndex, node.insertedFootnotes)); } /** @@ -649,7 +661,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { boolean canDeferOldFootnotes) { return getFootnoteSplit(activeNode.footnoteListIndex, activeNode.footnoteElementIndex, - activeNode.totalFootnotes, + activeNode.insertedFootnotes, availableLength, canDeferOldFootnotes); } @@ -714,10 +726,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { int prevIndex = -1; int index = -1; - while (!(somethingAdded && splitLength > availableLength)) { - if (!somethingAdded) { - somethingAdded = true; - } else { + while (splitLength <= availableLength) { + if (somethingAdded) { prevSplitLength = splitLength; prevIndex = index; } @@ -733,6 +743,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // element is a box splitLength += element.getWidth(); boxPreceding = true; + if (splitLength > prevSplitLength) { + // and it is non-empty + somethingAdded = true; + } } else if (element.isGlue()) { // element is a glue if (boxPreceding) { @@ -749,6 +763,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { index = noteListIterator.previousIndex(); break; } + boxPreceding = false; } } } @@ -758,7 +773,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // page here // if prevSplitLength is > 0 we can insert some footnote content in this page // and insert the remaining in the following one - //TODO: check this conditional, as the first one is always false...? if (!somethingAdded) { // there was not enough space to add a piece of the first new footnote // this is not a good break @@ -781,7 +795,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { if (difference > 0) { int maxAdjustment = totalStretch - activeNode.totalStretch; // add the footnote separator stretch if some footnote content will be added - if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) { + if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) { maxAdjustment += footnoteSeparatorLength.getStretch(); } if (maxAdjustment > 0) { @@ -792,7 +806,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } else if (difference < 0) { int maxAdjustment = totalShrink - activeNode.totalShrink; // add the footnote separator shrink if some footnote content will be added - if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) { + if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) { maxAdjustment += footnoteSeparatorLength.getShrink(); } if (maxAdjustment > 0) { @@ -866,7 +880,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { for (KnuthPageNode node = (KnuthPageNode) getNode(i); node != null; node = (KnuthPageNode) node.next) { - if (node.totalFootnotes < totalFootnotesLength) { + if (node.insertedFootnotes < totalFootnotesLength) { // layout remaining footnote bodies createFootnotePages(node); } @@ -876,7 +890,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { private void createFootnotePages(KnuthPageNode lastNode) { - insertedFootnotesLength = lastNode.totalFootnotes; + insertedFootnotesLength = lastNode.insertedFootnotes; footnoteListIndex = lastNode.footnoteListIndex; footnoteElementIndex = lastNode.footnoteElementIndex; int availableBPD = getLineWidth(lastNode.line); @@ -902,7 +916,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // cannot add any content: create a new node and start again KnuthPageNode node = (KnuthPageNode) createNode(lastNode.position, prevNode.line + 1, 1, - insertedFootnotesLength - prevNode.totalFootnotes, + insertedFootnotesLength - prevNode.insertedFootnotes, 0, 0, 0, 0, 0, 0, 0, prevNode); @@ -916,7 +930,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // create the last node KnuthPageNode node = (KnuthPageNode) createNode(lastNode.position, prevNode.line + 1, 1, - totalFootnotesLength - prevNode.totalFootnotes, 0, 0, + totalFootnotesLength - prevNode.insertedFootnotes, 0, 0, 0, 0, 0, 0, 0, prevNode); addNode(node.line, node); diff --git a/test/layoutengine/standard-testcases/footnote_jira2106.xml b/test/layoutengine/standard-testcases/footnote_jira2106.xml new file mode 100644 index 000000000..8acc91f35 --- /dev/null +++ b/test/layoutengine/standard-testcases/footnote_jira2106.xml @@ -0,0 +1,52 @@ + + + + + +

+ Test for FOP-2106: footnote must be positioned on the same page as the inline reference (not + on the page before). +

+
+ + + + + + + + + + Page 1 line 1 + Page 1 line 2 + Page 2 line 1 + Page 3 line + 1*Footnote should be on page + 3 + + + + + + + + + + +
-- cgit v1.2.3 From 27210ad8307d879b5308450c55bbf80b52f7c6aa Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 8 Oct 2013 15:41:25 +0000 Subject: Do not log a warning when .notdef has been assigned to an unused code of AdobeStandardEncoding git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1530313 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java b/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java index 8ef172875..79753f3f8 100644 --- a/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java +++ b/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java @@ -28,6 +28,8 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.fonts.Glyphs; + import org.apache.fop.fonts.NamedCharacter; import org.apache.fop.fonts.type1.AFMParser.ValueHandler; @@ -102,9 +104,10 @@ abstract class CharMetricsHandler { AFMCharMetrics chm = defaultHandler.parse(line, stack, afmFileName); NamedCharacter namedChar = chm.getCharacter(); if (namedChar != null) { - int codePoint = AdobeStandardEncoding.getAdobeCodePoint(namedChar.getName()); - if (chm.getCharCode() != codePoint) { - LOG.info(afmFileName + ": named character '" + namedChar.getName() + "'" + String charName = namedChar.getName(); + int codePoint = AdobeStandardEncoding.getAdobeCodePoint(charName); + if (chm.getCharCode() != codePoint && !Glyphs.NOTDEF.equals(charName)) { + LOG.info(afmFileName + ": named character '" + charName + "'" + " has an incorrect code point: " + chm.getCharCode() + ". Changed to " + codePoint); chm.setCharCode(codePoint); -- cgit v1.2.3 From c183dd2ece0d12fb156be6d9f5134a20d144ce73 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 9 Oct 2013 06:50:56 +0000 Subject: Removed unused variables git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1530507 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/cli/CommandLineOptions.java | 1 - src/java/org/apache/fop/fonts/apps/PFMReader.java | 3 --- src/java/org/apache/fop/fonts/apps/TTFReader.java | 3 --- 3 files changed, 7 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 627b95a47..20fcae223 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -144,7 +144,6 @@ public class CommandLineOptions { * Construct a command line option object. */ public CommandLineOptions() { - LogFactory logFactory = LogFactory.getFactory(); log = LogFactory.getLog("FOP"); } diff --git a/src/java/org/apache/fop/fonts/apps/PFMReader.java b/src/java/org/apache/fop/fonts/apps/PFMReader.java index 8ee6ff9b6..dd7f7343c 100644 --- a/src/java/org/apache/fop/fonts/apps/PFMReader.java +++ b/src/java/org/apache/fop/fonts/apps/PFMReader.java @@ -29,8 +29,6 @@ import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.apache.commons.logging.LogFactory; - import org.apache.fop.Version; import org.apache.fop.fonts.type1.PFMFile; @@ -91,7 +89,6 @@ public class PFMReader extends AbstractFontReader { Map options = new java.util.HashMap(); String[] arguments = parseArguments(options, args); - LogFactory logFactory = LogFactory.getFactory(); determineLogLevel(options); PFMReader app = new PFMReader(); diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java index fc003d201..f8c7360a4 100644 --- a/src/java/org/apache/fop/fonts/apps/TTFReader.java +++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java @@ -32,8 +32,6 @@ import org.w3c.dom.Element; import org.xml.sax.Attributes; import org.xml.sax.SAXException; -import org.apache.commons.logging.LogFactory; - import org.apache.fop.Version; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.FontUtil; @@ -123,7 +121,6 @@ public class TTFReader extends AbstractFontReader { Map options = new java.util.HashMap(); String[] arguments = parseArguments(options, args); - LogFactory logFactory = LogFactory.getFactory(); determineLogLevel(options); TTFReader app = new TTFReader(); -- cgit v1.2.3 From 72d4de19de6175a92213e3c05e11b71dd1ed2714 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Sun, 13 Oct 2013 23:31:46 +0000 Subject: FOP-1872: External document is clipped when rotated; patch submitted by Thanasis Giannimaras. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1531769 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/layoutmgr/ExternalDocumentLayoutManager.java | 10 ++++++++-- .../standard-testcases/fox_external-document_2.xml | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java index 7cbe33f26..aae5a6b30 100644 --- a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java @@ -242,8 +242,14 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan pv.setPage(pageArea); RegionViewport rv = new RegionViewport(referenceRect); - rv.setIPD(referenceRect.width); - rv.setBPD(referenceRect.height); + + if (pageSeq.getReferenceOrientation() % 180 == 0) { + rv.setIPD(referenceRect.width); + rv.setBPD(referenceRect.height); + } else { + rv.setIPD(referenceRect.height); + rv.setBPD(referenceRect.width); + } rv.setClip(true); BodyRegion body = new BodyRegion(Constants.FO_REGION_BODY, diff --git a/test/layoutengine/standard-testcases/fox_external-document_2.xml b/test/layoutengine/standard-testcases/fox_external-document_2.xml index b2f397580..29485ed72 100644 --- a/test/layoutengine/standard-testcases/fox_external-document_2.xml +++ b/test/layoutengine/standard-testcases/fox_external-document_2.xml @@ -71,8 +71,8 @@ - - + + -- cgit v1.2.3 From 39e331287690a4ba25b19cbf1d9af413c3e81aca Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 16 Oct 2013 23:31:17 +0000 Subject: FOP-2296: SVG fill pattern not visible with encryption; patch submitted by Simon Steiner git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1532940 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/pdf/PDFPattern.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java index 46a6a7378..b9e042815 100644 --- a/src/java/org/apache/fop/pdf/PDFPattern.java +++ b/src/java/org/apache/fop/pdf/PDFPattern.java @@ -269,13 +269,14 @@ public class PDFPattern extends PDFPathPaint { pdfStream = new PDFStream(); pdfStream.setDocument(getDocumentSafely()); pdfStream.add(this.patternDataStream.toString()); + pdfStream.setObjectNumber(getObjectNumber()); pdfStream.getFilterList().addDefaultFilters( getDocument().getFilterMap(), PDFFilterList.CONTENT_FILTER); + getDocument().applyEncryption(pdfStream); encodedStream = pdfStream.encodeStream(); p.append(pdfStream.getFilterList().buildFilterDictEntries()); - p.append("/Length " + (encodedStream.getSize() + 1) - + " \n"); + p.append("/Length " + encodedStream.getSize() + " \n"); } } else { -- cgit v1.2.3 From bd45787c63fcae138d6cdb28a1250396ccebbda0 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 21 Oct 2013 13:24:55 +0000 Subject: First bits of support for fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1534143 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 164 ++++++-------- .../apache/fop/layoutmgr/LayoutManagerMapping.java | 12 +- .../fop/layoutmgr/inline/ICLayoutManager.java | 54 ----- .../inline/InlineContainerLayoutManager.java | 237 +++++++++++++++++++++ .../standard-testcases/inline-container_basic.xml | 56 +++++ 5 files changed, 361 insertions(+), 162 deletions(-) delete mode 100644 src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java create mode 100644 src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java create mode 100644 test/layoutengine/standard-testcases/inline-container_basic.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index 748eb593a..d3a80eb3f 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -29,6 +29,7 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -37,78 +38,72 @@ import org.apache.fop.traits.Direction; import org.apache.fop.traits.WritingMode; import org.apache.fop.traits.WritingModeTraits; -/** - * Class modelling the - * fo:inline-container object. - */ public class InlineContainer extends FObj { - // The value of FO traits (refined properties) that apply to fo:inline-container. - private Length alignmentAdjust; - private int alignmentBaseline; - private Length baselineShift; + private LengthRangeProperty inlineProgressionDimension; private LengthRangeProperty blockProgressionDimension; + private int overflow; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginInline commonMarginInline; - private int clip; - private int dominantBaseline; - private LengthRangeProperty inlineProgressionDimension; + private Numeric referenceOrientation; + private int displayAlign; private KeepProperty keepTogether; + private KeepProperty keepWithNext; + private KeepProperty keepWithPrevious; private SpaceProperty lineHeight; - private int overflow; - private Numeric referenceOrientation; + private Length alignmentAdjust; + private int alignmentBaseline; + private Length baselineShift; + private int dominantBaseline; private WritingModeTraits writingModeTraits; - // Unused but valid items, commented out for performance: - // private CommonRelativePosition commonRelativePosition; - // private int displayAlign; - // private Length height; - // private KeepProperty keepWithNext; - // private KeepProperty keepWithPrevious; - // private Length width; - // End of FO trait values /** used for FO validation */ - private boolean blockItemFound = false; + private boolean blockItemFound; + private CommonFont commonFont; /** - * Base constructor + * Creates a new instance. * - * @param parent {@link FONode} that is the parent of this object + * @param parent the parent of this inline-container */ public InlineContainer(FONode parent) { super(parent); } - /** {@inheritDoc} */ + @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonFont = pList.getFontProps(); // TODO get directly from parent? alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginInline = pList.getMarginInlineProps(); - clip = pList.get(PR_CLIP).getEnum(); + displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum(); dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); + keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); + keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); overflow = pList.get(PR_OVERFLOW).getEnum(); referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); } /** * {@inheritDoc} *
XSL Content Model: marker* (%block;)+ */ + @Override protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { if (localName.equals("marker")) { if (blockItemFound) { - nodesOutOfOrderError(loc, "fo:marker", "(%block;)"); + nodesOutOfOrderError(loc, "fo:marker", "(%block;)+"); } } else if (!isBlockItem(nsURI, localName)) { invalidChildError(loc, nsURI, localName); @@ -118,142 +113,109 @@ public class InlineContainer extends FObj { } } - /** {@inheritDoc} */ + @Override public void endOfNode() throws FOPException { if (!blockItemFound) { missingChildElementError("marker* (%block;)+"); } } - /** @return the "alignment-adjust" FO trait */ - public Length getAlignmentAdjust() { - return alignmentAdjust; + /** {@inheritDoc} */ + public String getLocalName() { + return "inline-container"; } - /** @return the "alignment-baseline" FO trait */ - public int getAlignmentBaseline() { - return alignmentBaseline; + /** + * {@inheritDoc} + * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER} + */ + public int getNameId() { + return FO_INLINE_CONTAINER; } - /** @return the "baseline-shift" FO trait */ - public Length getBaselineShift() { - return baselineShift; + public LengthRangeProperty getInlineProgressionDimension() { + return inlineProgressionDimension; } - /** @return the "block-progression-dimension" FO trait */ public LengthRangeProperty getBlockProgressionDimension() { return blockProgressionDimension; } - /** @return the "clip" FO trait */ - public int getClip() { - return clip; + public int getOverflow() { + return overflow; } - /**@return Returns the {@link CommonBorderPaddingBackground} */ public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { return this.commonBorderPaddingBackground; } - /** @return Returns the {@link CommonMarginInline} */ public CommonMarginInline getCommonMarginInline() { return this.commonMarginInline; } - /** @return the "dominant-baseline" FO trait */ - public int getDominantBaseline() { - return dominantBaseline; + public int getReferenceOrientation() { + return referenceOrientation.getValue(); } - /** @return the "keep-together" FO trait */ - public KeepProperty getKeepTogether() { - return keepTogether; + public int getDisplayAlign() { + return this.displayAlign; } - /** @return the "inline-progression-dimension" FO trait */ - public LengthRangeProperty getInlineProgressionDimension() { - return inlineProgressionDimension; + public KeepProperty getKeepTogether() { + return keepTogether; } - /** @return the "line-height" FO trait */ public SpaceProperty getLineHeight() { return lineHeight; } - /** @return the "overflow" FO trait */ - public int getOverflow() { - return overflow; + public Length getAlignmentAdjust() { + return alignmentAdjust; } - /** @return the "reference-orientation" FO trait */ - public int getReferenceOrientation() { - return referenceOrientation.getValue(); + public int getAlignmentBaseline() { + return alignmentBaseline; + } + + public Length getBaselineShift() { + return baselineShift; + } + + public int getDominantBaseline() { + return dominantBaseline; + } + + public WritingMode getWritingMode() { + return writingModeTraits.getWritingMode(); } - /** - * Obtain inline progression direction. - * @return the inline progression direction - */ public Direction getInlineProgressionDirection() { return writingModeTraits.getInlineProgressionDirection(); } - /** - * Obtain block progression direction. - * @return the block progression direction - */ public Direction getBlockProgressionDirection() { return writingModeTraits.getBlockProgressionDirection(); } - /** - * Obtain column progression direction. - * @return the column progression direction - */ public Direction getColumnProgressionDirection() { return writingModeTraits.getColumnProgressionDirection(); } - /** - * Obtain row progression direction. - * @return the row progression direction - */ public Direction getRowProgressionDirection() { return writingModeTraits.getRowProgressionDirection(); } - /** - * Obtain (baseline) shift direction. - * @return the (baseline) shift direction - */ public Direction getShiftDirection() { return writingModeTraits.getShiftDirection(); } - /** - * Obtain writing mode. - * @return the writing mode - */ - public WritingMode getWritingMode() { - return writingModeTraits.getWritingMode(); - } - - /** {@inheritDoc} */ - public String getLocalName() { - return "inline-container"; - } - - /** - * {@inheritDoc} - * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER} - */ - public int getNameId() { - return FO_INLINE_CONTAINER; - } - @Override public boolean isDelimitedTextRangeBoundary(int boundary) { return false; } + public CommonFont getCommonFont() { + return commonFont; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 292251a84..0e333d219 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -73,7 +73,7 @@ import org.apache.fop.layoutmgr.inline.CharacterLayoutManager; import org.apache.fop.layoutmgr.inline.ContentLayoutManager; import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager; import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager; -import org.apache.fop.layoutmgr.inline.ICLayoutManager; +import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.layoutmgr.inline.InstreamForeignObjectLM; import org.apache.fop.layoutmgr.inline.LeaderLayoutManager; @@ -257,9 +257,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker { /** a layout manager maker */ public static class InlineLayoutManagerMaker extends Maker { /** {@inheritDoc} */ - public void make(FONode node, List lms) { - lms.add(new InlineLayoutManager((InlineLevel) node)); - } + public void make(FONode node, List lms) { + lms.add(new InlineLayoutManager((InlineLevel) node)); + } } /** a layout manager maker */ @@ -274,9 +274,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { public static class InlineContainerLayoutManagerMaker extends Maker { /** {@inheritDoc} */ public void make(FONode node, List lms) { - ArrayList childList = new ArrayList(); - super.make(node, childList); - lms.add(new ICLayoutManager((InlineContainer) node, childList)); + lms.add(new InlineContainerLayoutManager((InlineContainer) node)); } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java deleted file mode 100644 index 7fe90f63c..000000000 --- a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java +++ /dev/null @@ -1,54 +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.layoutmgr.inline; - -// Java -import java.util.List; - -// FOP -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.fo.flow.InlineContainer; -/** - * This creates a single inline container area after - * laying out the child block areas. All footnotes, floats - * and id areas are maintained for later retrieval. - */ -public class ICLayoutManager extends LeafNodeLayoutManager { - private List childrenLM; - - /** - * Construct inline container layout manager. - * @param node inline container FO node - * @param childLM child layout manager - */ - public ICLayoutManager(InlineContainer node, List childLM) { - super(node); - childrenLM = childLM; - } - - /** - * @param index an integer - * @return an inline area or null - */ - public InlineArea get(int index) { - return null; - } - -} diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java new file mode 100644 index 000000000..548225ad7 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -0,0 +1,237 @@ +/* + * 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.inline; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.Trait; +import org.apache.fop.area.inline.Container; +import org.apache.fop.area.inline.InlineViewport; +import org.apache.fop.fo.flow.InlineContainer; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.LengthRangeProperty; +import org.apache.fop.fo.properties.Property; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.InlineKnuthSequence; +import org.apache.fop.layoutmgr.KnuthPossPosIter; +import org.apache.fop.layoutmgr.KnuthSequence; +import org.apache.fop.layoutmgr.LayoutContext; +import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.ListElement; +import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.PositionIterator; +import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.TraitSetter; + +/** + * This creates a single inline container area after + * laying out the child block areas. All footnotes, floats + * and id areas are maintained for later retrieval. + */ +public class InlineContainerLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager { + + private CommonBorderPaddingBackground borderProps; + private int alignmentBaseline = EN_BASELINE; + private int contentAreaIPD; + private int contentAreaBPD; + + private List childElements; + private InlineViewport currentViewport; + private Container referenceArea; + + public InlineContainerLayoutManager(InlineContainer node) { + super(node); + } + + @Override + public void initialize() { + InlineContainer node = (InlineContainer) fobj; + borderProps = node.getCommonBorderPaddingBackground(); + } + + @Override + public List getNextKnuthElements(LayoutContext context, int alignment) { + InlineContainer ic = (InlineContainer) fobj; + contentAreaIPD = getLength(ic.getInlineProgressionDimension()); + contentAreaBPD = getLength(ic.getBlockProgressionDimension()); + LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? + childLC.setRefIPD(contentAreaIPD); + childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? + Position position = new Position(this, 0); + KnuthSequence knuthSequence = new InlineKnuthSequence(); + knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); + List knuthElements = new ArrayList(1); + knuthElements.add(knuthSequence); + setFinished(true); + return knuthElements; + } + + private int getLength(LengthRangeProperty property) { + Property optimum = property.getOptimum(this); // TODO percent base context + if (optimum.isAuto()) { + throw new UnsupportedOperationException("auto dimension not supported"); + } + return optimum.getLength().getValue(this); // TODO percent base context + } + + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { + List allChildElements = new LinkedList(); + LayoutManager childLM; + while ((childLM = getChildLM()) != null) { + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? + childLC.setRefIPD(layoutContext.getRefIPD()); + @SuppressWarnings("unchecked") + List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + allChildElements.addAll(childElements); + // TODO breaks, keeps, empty content + } + SpaceResolver.resolveElementList(allChildElements); + // TODO break-before, break-after + return allChildElements; + } + + @Override + public void addAreas(PositionIterator posIter, LayoutContext context) { + Position inlineContainerPosition = null; + while (posIter.hasNext()) { + Position pos = posIter.next(); + if (pos.getLM() == this) { + inlineContainerPosition = pos; + } + } + addId(); +// addMarkersToPage( +// true, +// true, +// lastPos == null || isLast(lastPos)); + + if (inlineContainerPosition != null) { + LayoutManager childLM; + KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); + while ((childLM = childPosIter.getNextChildLM()) != null) { + LayoutContext childLC = LayoutContext.copyOf(context); // TODO correct? + childLM.addAreas(childPosIter, childLC); + } + } + +// addMarkersToPage( +// false, +// true, +// lastPos == null || isLast(lastPos)); + +// boolean isLast = (context.isLastArea() && prevLM == lastChildLM); +// context.setFlags(LayoutContext.LAST_AREA, isLast); + } + + @Override + public Area getParentArea(Area childArea) { + if (referenceArea == null) { + referenceArea = new Container(); + referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); + TraitSetter.setProducerID(referenceArea, fobj.getId()); + referenceArea.setIPD(contentAreaIPD); + currentViewport = new InlineViewport(referenceArea); + currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); + currentViewport.setIPD(getContentAreaIPD()); + currentViewport.setBPD(getContentAreaBPD()); + TraitSetter.setProducerID(currentViewport, fobj.getId()); + TraitSetter.addBorders(currentViewport, + borderProps, + false, false, false, false, this); + TraitSetter.addPadding(currentViewport, + borderProps, + false, false, false, false, this); + TraitSetter.addBackground(currentViewport, + borderProps, + this); + currentViewport.setClip(needClip()); + currentViewport.setContentPosition( + new java.awt.geom.Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); + getParent().addChildArea(currentViewport); + } + return referenceArea; + } + + @Override + public int getContentAreaIPD() { + return contentAreaIPD; + } + + @Override + public int getContentAreaBPD() { + return contentAreaBPD; + } + + @Override + public void addChildArea(Area childArea) { + referenceArea.addBlock((Block) childArea); + } + + private boolean needClip() { + int overflow = ((InlineContainer) fobj).getOverflow(); + return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); + } + + protected AlignmentContext makeAlignmentContext(LayoutContext context) { + InlineContainer ic = (InlineContainer) fobj; + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); + Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); + return new AlignmentContext(fs, ic.getLineHeight().getOptimum(this).getLength().getValue(this), // TODO + context.getWritingMode()); + } + + public List addALetterSpaceTo(List oldList) { + throw new UnsupportedOperationException("Not implemented"); + } + + public List addALetterSpaceTo(List oldList, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + + public String getWordChars(Position pos) { + throw new UnsupportedOperationException("Not implemented"); + } + + public void hyphenate(Position pos, HyphContext hyphContext) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean applyChanges(List oldList) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean applyChanges(List oldList, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + + public List getChangedKnuthElements(List oldList, int alignment, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + +} diff --git a/test/layoutengine/standard-testcases/inline-container_basic.xml b/test/layoutengine/standard-testcases/inline-container_basic.xml new file mode 100644 index 000000000..b2dfcc6e5 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_basic.xml @@ -0,0 +1,56 @@ + + + + + +

+ Test for a basic implementation of fo:inline-container. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 480edcfb80906fbad780060093c7e943b91f8430 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Mon, 21 Oct 2013 14:54:09 +0000 Subject: FOP-2299: Non Unicode named glyphs not loaded for Type1 fonts git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1534195 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/type1/AFMFile.java | 2 +- src/java/org/apache/fop/fonts/type1/Type1FontLoader.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/type1/AFMFile.java b/src/java/org/apache/fop/fonts/type1/AFMFile.java index f6bc3b163..2aa718ea0 100644 --- a/src/java/org/apache/fop/fonts/type1/AFMFile.java +++ b/src/java/org/apache/fop/fonts/type1/AFMFile.java @@ -319,7 +319,7 @@ public class AFMFile { */ public void addCharMetrics(AFMCharMetrics metrics) { String name = metrics.getCharName(); - if (metrics.getUnicodeSequence() == null) { + if (metrics.getUnicodeSequence() == null && name.equals(".notdef")) { //Ignore as no Unicode assignment is possible return; } diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java index 853e23eb5..dddf2a3a3 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -393,10 +393,7 @@ public class Type1FontLoader extends FontLoader { List chars = afm.getCharMetrics(); for (AFMCharMetrics charMetrics : chars) { if (charMetrics.getCharCode() >= 0) { - String u = charMetrics.getUnicodeSequence(); - if (u != null && u.length() == 1) { - mappingCount++; - } + mappingCount++; } } // ...and now build the table. @@ -409,6 +406,10 @@ public class Type1FontLoader extends FontLoader { String unicodes = charMetrics.getUnicodeSequence(); if (unicodes == null) { log.info("No Unicode mapping for glyph: " + charMetrics); + table[idx] = charMetrics.getCharCode(); + idx++; + table[idx] = charMetrics.getCharCode(); + idx++; } else if (unicodes.length() == 1) { table[idx] = charMetrics.getCharCode(); idx++; -- cgit v1.2.3 From 6c5966390eb0fdeb8c4b7cc5231e6244cdd53512 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Tue, 22 Oct 2013 10:06:12 +0000 Subject: FOP-2104: RTF renderer barfs when fo:table-row is missing inside fo-table-header git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1534582 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/render/rtf/RTFHandler.java | 42 +++++++++---- .../fop/render/rtf/RTFPlaceHolderHelper.java | 72 ++++++++++++++++++++++ .../render/rtf/rtflib/tools/BuilderContext.java | 71 ++++++++++++++++++--- 3 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index dfeff65f1..73fa3504c 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -157,6 +157,7 @@ public class RTFHandler extends FOEventHandler { private PercentContext percentManager = new PercentContext(); + /** * Creates a new RTF structure handler. * @param userAgent the FOUserAgent for this process @@ -270,7 +271,7 @@ public class RTFHandler extends FOEventHandler { return; } else { - builderContext.popContainer(); + builderContext.popContainer(RtfSection.class, this); this.pagemaster = null; } } @@ -377,10 +378,10 @@ public class RTFHandler extends FOEventHandler { //just do nothing } else if (regionBefore != null && fl.getFlowName().equals(regionBefore.getRegionName())) { - builderContext.popContainer(); + builderContext.popContainer(RtfBefore.class, this); } else if (regionAfter != null && fl.getFlowName().equals(regionAfter.getRegionName())) { - builderContext.popContainer(); + builderContext.popContainer(RtfAfter.class, this); } } catch (Exception e) { log.error("endFlow: " + e.getMessage()); @@ -571,7 +572,7 @@ public class RTFHandler extends FOEventHandler { nestedTableDepth--; builderContext.popTableContext(); - builderContext.popContainer(); + builderContext.popContainer(RtfTable.class, this); } /** {@inheritDoc} */ @@ -605,21 +606,25 @@ public class RTFHandler extends FOEventHandler { /** {@inheritDoc} */ public void startHeader(TableHeader header) { + builderContext.pushPart(header); startPart(header); } /** {@inheritDoc} */ public void endHeader(TableHeader header) { + builderContext.popPart(header.getClass(), this); endPart(header); } /** {@inheritDoc} */ public void startFooter(TableFooter footer) { + builderContext.pushPart(footer); startPart(footer); } /** {@inheritDoc} */ public void endFooter(TableFooter footer) { + builderContext.popPart(footer.getClass(), this); endPart(footer); } @@ -711,11 +716,13 @@ public class RTFHandler extends FOEventHandler { * {@inheritDoc} */ public void startBody(TableBody body) { + builderContext.pushPart(body); startPart(body); } /** {@inheritDoc} */ public void endBody(TableBody body) { + builderContext.popPart(TableBody.class, this); endPart(body); } @@ -784,7 +791,7 @@ public class RTFHandler extends FOEventHandler { } - builderContext.popContainer(); + builderContext.popContainer(RtfTableRow.class, this); builderContext.getTableContext().decreaseRowSpannings(); } @@ -893,7 +900,7 @@ public class RTFHandler extends FOEventHandler { throw new RuntimeException(e.getMessage()); } - builderContext.popContainer(); + builderContext.popContainer(RtfTableCell.class, this); builderContext.getTableContext().selectNextColumn(); } @@ -929,7 +936,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfList.class, this); } /** {@inheritDoc} */ @@ -976,7 +983,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfListItem.class, this); } /** {@inheritDoc} */ @@ -1005,7 +1012,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfListItemLabel.class, this); } /** {@inheritDoc} */ @@ -1070,7 +1077,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfHyperLink.class, this); } /** {@inheritDoc} */ @@ -1306,7 +1313,7 @@ public class RTFHandler extends FOEventHandler { return; } - builderContext.popContainer(); + builderContext.popContainer(RtfFootnote.class, this); } /** {@inheritDoc} */ @@ -1687,6 +1694,19 @@ public class RTFHandler extends FOEventHandler { } } + /** + * Closes any mismatched tags that are detected in the RTF structure. + * @param containerClass The class representing the tag to close. + * @return Determines whether the tag mismatch has been handled. + */ + public boolean endContainer(Class containerClass) { + if (containerClass == RtfTableRow.class) { + endRow(null); + return true; + } + return false; + } + /** * Calls the event handlers for the passed FONode and all its elements. * diff --git a/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java new file mode 100644 index 000000000..c31c68db1 --- /dev/null +++ b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.rtf; + +import org.apache.fop.render.rtf.rtflib.exceptions.RtfException; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfAttributes; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTable; +import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTableRow; +import org.apache.fop.render.rtf.rtflib.tools.BuilderContext; + +/** + * This class creates objects which are missing from the XSL:FO but are required + * by the RTF format. + */ +public class RTFPlaceHolderHelper { + /** The context object for building the RTF */ + private BuilderContext builderContext; + + /** + * Creates a new instance for the RTF place holder which attempts to resolve + * mismatches in structure between XSL:FO and RTF. + * @param builderContext The builder context + */ + public RTFPlaceHolderHelper(BuilderContext builderContext) { + this.builderContext = builderContext; + } + + /** + * A method to create an object which is missing and required from the + * RTF structure. + * @param containerClass The class which is missing + * @throws Exception + */ + public void createRTFPlaceholder(Class containerClass) throws RtfException { + if (containerClass == RtfTableRow.class) { + createRtfTableRow(); + } + } + + private void createRtfTableRow() throws RtfException { + try { + RtfContainer element = builderContext.getContainer(RtfTable.class, true, null); + if (element != null && element instanceof RtfTable) { + RtfTable table = (RtfTable)element; + RtfAttributes attribs = new RtfAttributes(); + RtfTableRow newRow = table.newTableRow(attribs); + builderContext.pushContainer(newRow); + builderContext.getTableContext().selectFirstColumn(); + } + } catch (Exception ex) { + throw new RtfException(ex.getMessage()); + } + } +} diff --git a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java index f0a29a0ab..6dcae5412 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java @@ -21,6 +21,12 @@ package org.apache.fop.render.rtf.rtflib.tools; import java.util.Stack; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.FObj; +import org.apache.fop.render.rtf.RTFHandler; +import org.apache.fop.render.rtf.RTFPlaceHolderHelper; import org.apache.fop.render.rtf.rtflib.exceptions.RtfException; import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfOptions; import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; @@ -38,6 +44,10 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer; */ public class BuilderContext { + + /** Static logging instance */ + protected static final Log LOG = LogFactory.getLog(BuilderContext.class.getName()); + /** stack of RtfContainers */ private final Stack containers = new Stack(); @@ -96,17 +106,22 @@ public class BuilderContext { * @throws RtfException if not caught */ public RtfContainer getContainer(Class containerClass, boolean required, - Object /*IBuilder*/ forWhichBuilder) throws RtfException { + Object forWhichBuilder) throws RtfException { // TODO what to do if the desired container is not at the top of the stack? // close top-of-stack container? - final RtfContainer result = (RtfContainer)getObjectFromStack(containers, + RtfContainer result = (RtfContainer)getObjectFromStack(containers, containerClass); if (result == null && required) { - throw new RtfException( - "No RtfContainer of class '" + containerClass.getName() - + "' available for '" + forWhichBuilder.getClass().getName() + "' builder" - ); + RTFPlaceHolderHelper placeHolderHelper = new RTFPlaceHolderHelper(this); + placeHolderHelper.createRTFPlaceholder(containerClass); + result = getContainer(containerClass, required, forWhichBuilder); + if (result == null) { + throw new RtfException( + "No RtfContainer of class '" + containerClass.getName() + + "' available for '" + forWhichBuilder.getClass().getName() + "' builder" + ); + } } return result; @@ -120,6 +135,14 @@ public class BuilderContext { containers.push(c); } + /** + * Push a Class representing a non-writeable section of the FO tree + * @param part the part + */ + public void pushPart(FObj part) { + containers.push(part); + } + /** * In some cases an RtfContainer must be replaced by another one on the * stack. This happens when handling nested fo:blocks for example: after @@ -142,8 +165,40 @@ public class BuilderContext { } /** pop the topmost RtfContainer from our stack */ - public void popContainer() { - containers.pop(); + public void popContainer(Class containerClass, RTFHandler handler) { + handlePop(containerClass, handler); + } + + /** pop the topmost part class from our stack */ + public void popPart(Class part, RTFHandler handler) { + handlePop(part, handler); + } + + /** + * This method checks for any tag mismatches between what is being closed + * and what exists on the stack. If a mismatch is found, then it will push + * the object back onto the stack and attempt to close the given section + * before retrying with the current pop task. + * @param aClass The class to be popped from the stack + * @param handler The RTF handler class to fix any mismatches + */ + private void handlePop(Class aClass, RTFHandler handler) { + Object object = containers.pop(); + if (object.getClass() != aClass) { + pushAndClose(aClass, object, handler); + } + } + + private void pushAndClose(Class aClass, Object object, RTFHandler handler) { + containers.push(object); + if (handler.endContainer(object.getClass())) { + popContainer(aClass, handler); + } else { + /* This should never happen unless a placeholder is not catered for + * in the RTFHandler.endContainer method. */ + LOG.warn("Unhandled RTF structure tag mismatch detected between " + + aClass.getSimpleName() + " and "+object.getClass().getSimpleName()); + } } /* push an IBuilder to our stack / -- cgit v1.2.3 From e82827107f0da059d01837b72d64dba1029f2b2e Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 22 Oct 2013 17:15:20 +0000 Subject: FOP-2306: use the URI of the source FO document rather than the config file as a default base URI git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1534704 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/cli/CommandLineOptions.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 20fcae223..a216e6b3c 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -21,8 +21,10 @@ package org.apache.fop.cli; // java import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.PrintStream; import java.net.URI; import java.util.Locale; @@ -45,6 +47,7 @@ import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.FopFactoryBuilder; import org.apache.fop.apps.FopFactoryConfig; import org.apache.fop.apps.MimeConstants; +import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.pdf.PDFAMode; import org.apache.fop.pdf.PDFEncryptionManager; import org.apache.fop.pdf.PDFEncryptionParams; @@ -1018,7 +1021,10 @@ public class CommandLineOptions { fopFactoryBuilder.setComplexScriptFeatures(useComplexScriptFeatures); } else { try { - fopFactoryBuilder = new FopConfParser(userConfigFile).getFopFactoryBuilder(); + InputStream userConfig = new FileInputStream(userConfigFile); + FopConfParser fopConfParser = new FopConfParser(userConfig, baseURI, + ResourceResolverFactory.createDefaultResourceResolver()); + fopFactoryBuilder = fopConfParser.getFopFactoryBuilder(); } catch (SAXException e) { throw new FOPException(e); } -- cgit v1.2.3 From 31037a0f70a013e5be7c7467590ec9e4f202342b Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 14:00:23 +0000 Subject: Parse back the container area from the area tree. Fixes AreaTreeParserTestCase. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535384 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/area/AreaTreeParser.java | 17 +++++++++++++++++ src/java/org/apache/fop/area/inline/Container.java | 15 +++++++-------- .../layoutmgr/inline/InlineContainerLayoutManager.java | 3 +-- 3 files changed, 25 insertions(+), 10 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index c79f975c1..e9f0ffb8c 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -62,6 +62,7 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.area.Trait.Background; import org.apache.fop.area.Trait.InternalLink; import org.apache.fop.area.inline.AbstractTextArea; +import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; import org.apache.fop.area.inline.InlineArea; @@ -195,6 +196,7 @@ public class AreaTreeParser { makers.put("space", new SpaceMaker()); makers.put("leader", new LeaderMaker()); makers.put("viewport", new InlineViewportMaker()); + makers.put("container", new ContainerMaker()); makers.put("image", new ImageMaker()); makers.put("foreignObject", new ForeignObjectMaker()); makers.put("bookmarkTree", new BookmarkTreeMaker()); @@ -863,6 +865,21 @@ public class AreaTreeParser { } } + private class ContainerMaker extends AbstractMaker { + + public void startElement(Attributes attributes) { + Container container = new Container(); + transferForeignObjects(attributes, container); + InlineViewport parent = (InlineViewport) areaStack.peek(); + parent.setContent(container); + areaStack.push(container); + } + + public void endElement() { + assertObjectOfClass(areaStack.pop(), Container.class); + } + } + private class InlineViewportMaker extends AbstractMaker { public void startElement(Attributes attributes) { diff --git a/src/java/org/apache/fop/area/inline/Container.java b/src/java/org/apache/fop/area/inline/Container.java index bc2acaa28..3d0060007 100644 --- a/src/java/org/apache/fop/area/inline/Container.java +++ b/src/java/org/apache/fop/area/inline/Container.java @@ -51,13 +51,12 @@ public class Container extends Area { public Container() { } - /** - * Add the block to this area. - * - * @param block the block area to add - */ - public void addBlock(Block block) { - blocks.add(block); + @Override + public void addChildArea(Area child) { + if (!(child instanceof Block)) { + throw new IllegalArgumentException("Container only accepts block areas"); + } + blocks.add((Block) child); } /** @@ -65,7 +64,7 @@ public class Container extends Area { * * @return the list of block areas */ - public List getBlocks() { + public List getBlocks() { return blocks; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 548225ad7..3e044ad38 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -24,7 +24,6 @@ import java.util.LinkedList; import java.util.List; import org.apache.fop.area.Area; -import org.apache.fop.area.Block; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; @@ -189,7 +188,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public void addChildArea(Area childArea) { - referenceArea.addBlock((Block) childArea); + referenceArea.addChildArea(childArea); } private boolean needClip() { -- cgit v1.2.3 From 69ff3a9270810958bbbcb414b60c7e2d460d3221 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 15:17:36 +0000 Subject: When inline-progression-dimension has been left to auto on fo:inline-container, fall back to the IPD of the nearest ancestor reference-area. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535410 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 15 +++++- .../layoutmgr/inline/InlineLevelEventProducer.java | 9 ++++ .../layoutmgr/inline/InlineLevelEventProducer.xml | 1 + .../inline-container_auto-ipd.xml | 61 ++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_auto-ipd.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 3e044ad38..fb342bd8e 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -75,7 +75,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { InlineContainer ic = (InlineContainer) fobj; - contentAreaIPD = getLength(ic.getInlineProgressionDimension()); + determineIPD(context); contentAreaBPD = getLength(ic.getBlockProgressionDimension()); LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? childLC.setRefIPD(contentAreaIPD); @@ -90,6 +90,19 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return knuthElements; } + private void determineIPD(LayoutContext layoutContext) { + LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); + Property optimum = ipd.getOptimum(this); // TODO percent base context + if (optimum.isAuto()) { + contentAreaIPD = layoutContext.getRefIPD(); + InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + eventProducer.inlineContainerAutoIPDNotSupported(this, contentAreaIPD / 1000f); + } else { + contentAreaIPD = optimum.getLength().getValue(this); // TODO percent base context + } + } + private int getLength(LengthRangeProperty property) { Property optimum = property.getOptimum(this); // TODO percent base context if (optimum.isAuto()) { diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java index 15284ae0a..332e14935 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java @@ -67,4 +67,13 @@ public interface InlineLevelEventProducer extends EventProducer { */ void lineOverflows(Object source, String elementName, int line, int overflowLength, Locator loc); + /** + * Auto IPD on inline-container is not supported. + * + * @param source the event source + * @param fallback the value in points that will be used as a fallback + * @event.severity WARN + */ + void inlineContainerAutoIPDNotSupported(Object source, float fallback); + } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml index 66d352eb7..8d699f6bc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml @@ -20,4 +20,5 @@ [ (See position {loc})| (See {#gatherContextInfo})| (No context info available)] fo:leader is set to "use-content" but has no content.{{locator}} The contents of {elementName} line {line} exceed the available area in the inline-progression direction by {overflowLength,choice,50000#{overflowLength} millipoints|50000<more than 50 points}.{{locator}} + A value of "auto" for the inline-progression-dimension property on fo:inline-container is not supported. Falling back to {fallback}pt.{{locator}} diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml new file mode 100644 index 000000000..48f5cd1cf --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml @@ -0,0 +1,61 @@ + + + + + +

+ When inline-progression-dimension has been left to auto on fo:inline-container, fall back to + the IPD of the nearest ancestor reference-area. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
-- cgit v1.2.3 From c925990c7c290876d6857a4cc02e362b0ed6ca72 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 15:36:12 +0000 Subject: Added support for block-progression-dimension="auto" git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535417 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 14 ++++--- .../standard-testcases/inline-container_bpd.xml | 46 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_bpd.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index fb342bd8e..107461c0a 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthPossPosIter; import org.apache.fop.layoutmgr.KnuthSequence; @@ -74,12 +75,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { - InlineContainer ic = (InlineContainer) fobj; determineIPD(context); - contentAreaBPD = getLength(ic.getBlockProgressionDimension()); LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? childLC.setRefIPD(contentAreaIPD); childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + determineBPD(); AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); @@ -103,12 +103,14 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - private int getLength(LengthRangeProperty property) { - Property optimum = property.getOptimum(this); // TODO percent base context + private void determineBPD() { + LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); + Property optimum = bpd.getOptimum(this); // TODO percent base context if (optimum.isAuto()) { - throw new UnsupportedOperationException("auto dimension not supported"); + contentAreaBPD = ElementListUtils.calcContentLength(childElements); + } else { + contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context } - return optimum.getLength().getValue(this); // TODO percent base context } private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml new file mode 100644 index 000000000..0a9d87442 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -0,0 +1,46 @@ + + + + + +

+ Checks that inline-progression-dimension on fo:inline-container is properly handled. +

+
+ + + + + + + + + + Before: + This text inside inline-container should fit on four lines. + After. + + + + + + + + +
-- cgit v1.2.3 From 5530937d762b0a15b48940c544a157769b1137a0 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 16:26:26 +0000 Subject: Issue a warning if the content of an inline-container overflows it in the block-progression-direction git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535430 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/InlineContainerLayoutManager.java | 12 +++++++++++- .../standard-testcases/inline-container_auto-ipd.xml | 2 +- .../layoutengine/standard-testcases/inline-container_bpd.xml | 12 +++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 107461c0a..152d16a3d 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.BlockLevelEventProducer; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthPossPosIter; @@ -106,10 +107,19 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); // TODO percent base context + int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { - contentAreaBPD = ElementListUtils.calcContentLength(childElements); + contentAreaBPD = actualBPD; } else { contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context + if (contentAreaBPD < actualBPD) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + eventProducer.viewportBPDOverflow(this, fobj.getName(), + actualBPD - contentAreaBPD, needClip(), canRecover, + fobj.getLocator()); + } } } diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml index 48f5cd1cf..0833d7f0d 100644 --- a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml +++ b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml @@ -42,7 +42,7 @@ - Before: + Before: Inside the inline-container. After. diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml index 0a9d87442..bd2dfdf9e 100644 --- a/test/layoutengine/standard-testcases/inline-container_bpd.xml +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -37,10 +37,20 @@ After. + + + Before: + This text overflows the inline-container in the + block-progression-direction. + After. + + - + + + -- cgit v1.2.3 From dc25857bb531326a321c930b7734626aaef3b909 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:09:21 +0000 Subject: Do space resolution on the children of the inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536418 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/layoutmgr/AreaAdditionUtil.java | 24 +++++++++--------- .../inline/InlineContainerLayoutManager.java | 29 ++++++++++------------ .../standard-testcases/inline-container_bpd.xml | 12 +++++++++ 3 files changed, 37 insertions(+), 28 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java index 0ed6cb69b..d731ab62a 100644 --- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java +++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java @@ -33,11 +33,11 @@ public final class AreaAdditionUtil { /** * Creates the child areas for the given layout manager. - * @param bslm the BlockStackingLayoutManager instance for which "addAreas" is performed. + * @param parentLM the parent layout manager * @param parentIter the position iterator * @param layoutContext the layout context */ - public static void addAreas(BlockStackingLayoutManager bslm, + public static void addAreas(AbstractLayoutManager parentLM, PositionIterator parentIter, LayoutContext layoutContext) { LayoutManager childLM; LayoutContext lc = LayoutContext.offspringOf(layoutContext); @@ -46,8 +46,8 @@ public final class AreaAdditionUtil { Position firstPos = null; Position lastPos = null; - if (bslm != null) { - bslm.addId(); + if (parentLM != null) { + parentLM.addId(); } // "unwrap" the NonLeafPositions stored in parentIter @@ -86,11 +86,11 @@ public final class AreaAdditionUtil { //doesn't give us that info. } - if (bslm != null) { - bslm.registerMarkers( + if (parentLM != null) { + parentLM.registerMarkers( true, - bslm.isFirst(firstPos), - bslm.isLast(lastPos)); + parentLM.isFirst(firstPos), + parentLM.isLast(lastPos)); } PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); @@ -113,11 +113,11 @@ public final class AreaAdditionUtil { childLM.addAreas(childPosIter, lc); } - if (bslm != null) { - bslm.registerMarkers( + if (parentLM != null) { + parentLM.registerMarkers( false, - bslm.isFirst(firstPos), - bslm.isLast(lastPos)); + parentLM.isFirst(firstPos), + parentLM.isLast(lastPos)); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 152d16a3d..3688bcff0 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelEventProducer; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; @@ -43,6 +44,7 @@ import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.ListElement; +import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceResolver; @@ -134,11 +136,20 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } + wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); // TODO break-before, break-after return allChildElements; } + private void wrapPositions(List elements) { + for (ListElement element : elements) { + Position position = new NonLeafPosition(this, element.getPosition()); + notifyPos(position); + element.setPosition(position); + } + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -148,26 +159,12 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen inlineContainerPosition = pos; } } - addId(); -// addMarkersToPage( -// true, -// true, -// lastPos == null || isLast(lastPos)); - if (inlineContainerPosition != null) { - LayoutManager childLM; + SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); - while ((childLM = childPosIter.getNextChildLM()) != null) { - LayoutContext childLC = LayoutContext.copyOf(context); // TODO correct? - childLM.addAreas(childPosIter, childLC); - } + AreaAdditionUtil.addAreas(this, childPosIter, context); } -// addMarkersToPage( -// false, -// true, -// lastPos == null || isLast(lastPos)); - // boolean isLast = (context.isLastArea() && prevLM == lastChildLM); // context.setFlags(LayoutContext.LAST_AREA, isLast); } diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml index bd2dfdf9e..e63172b15 100644 --- a/test/layoutengine/standard-testcases/inline-container_bpd.xml +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -45,12 +45,24 @@ After. + + + Before: + Block 1 + Block 2 + After. + + + + + -- cgit v1.2.3 From 8c5aef924ae27549bac3d34057e5e36aaf0bca81 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:13:14 +0000 Subject: Slightly simplified looping through positions in addAreas git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536424 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 3688bcff0..a51d64c2c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -154,16 +154,18 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; while (posIter.hasNext()) { - Position pos = posIter.next(); - if (pos.getLM() == this) { - inlineContainerPosition = pos; - } - } - if (inlineContainerPosition != null) { - SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); - KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); - AreaAdditionUtil.addAreas(this, childPosIter, context); + /* + * Should iterate only once, but hasNext must be called twice for its + * side-effects to apply and the iterator to switch to the next LM. + */ + assert inlineContainerPosition == null; + inlineContainerPosition = posIter.next(); + assert inlineContainerPosition.getLM() == this; } + assert inlineContainerPosition != null; + SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); + KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); + AreaAdditionUtil.addAreas(this, childPosIter, context); // boolean isLast = (context.isLastArea() && prevLM == lastChildLM); // context.setFlags(LayoutContext.LAST_AREA, isLast); -- cgit v1.2.3 From 0169e567a969752cf36139831318187a1e63c8e9 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:15:35 +0000 Subject: Added support for percentage dimensions git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536425 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 27 ++++++---- .../inline-container_auto-ipd.xml | 61 ---------------------- .../inline-container_ipd-auto.xml | 61 ++++++++++++++++++++++ .../inline-container_ipd-percent.xml | 61 ++++++++++++++++++++++ 4 files changed, 138 insertions(+), 72 deletions(-) delete mode 100644 test/layoutengine/standard-testcases/inline-container_auto-ipd.xml create mode 100644 test/layoutengine/standard-testcases/inline-container_ipd-auto.xml create mode 100644 test/layoutengine/standard-testcases/inline-container_ipd-percent.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index a51d64c2c..7e2b186be 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -95,32 +95,37 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private void determineIPD(LayoutContext layoutContext) { LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); - Property optimum = ipd.getOptimum(this); // TODO percent base context + Property optimum = ipd.getOptimum(this); if (optimum.isAuto()) { contentAreaIPD = layoutContext.getRefIPD(); InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get( fobj.getUserAgent().getEventBroadcaster()); eventProducer.inlineContainerAutoIPDNotSupported(this, contentAreaIPD / 1000f); } else { - contentAreaIPD = optimum.getLength().getValue(this); // TODO percent base context + contentAreaIPD = optimum.getLength().getValue(this); } } private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); - Property optimum = bpd.getOptimum(this); // TODO percent base context + Property optimum = bpd.getOptimum(this); int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { contentAreaBPD = actualBPD; } else { - contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context - if (contentAreaBPD < actualBPD) { - BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( - fobj.getUserAgent().getEventBroadcaster()); - boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); - eventProducer.viewportBPDOverflow(this, fobj.getName(), - actualBPD - contentAreaBPD, needClip(), canRecover, - fobj.getLocator()); + double bpdValue = optimum.getLength().getNumericValue(this); + if (bpdValue < 0) { + contentAreaBPD = actualBPD; + } else { + contentAreaBPD = (int) Math.round(bpdValue); + if (contentAreaBPD < actualBPD) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + eventProducer.viewportBPDOverflow(this, fobj.getName(), + actualBPD - contentAreaBPD, needClip(), canRecover, + fobj.getLocator()); + } } } } diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml deleted file mode 100644 index 0833d7f0d..000000000 --- a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - -

- When inline-progression-dimension has been left to auto on fo:inline-container, fall back to - the IPD of the nearest ancestor reference-area. -

-
- - - - - - - - - - Before: - Text inside inline-container. - After. - - - - - - - Before: - Inside the inline-container. - After. - - - - - - - - - - - - - -
diff --git a/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml b/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml new file mode 100644 index 000000000..0833d7f0d --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml @@ -0,0 +1,61 @@ + + + + + +

+ When inline-progression-dimension has been left to auto on fo:inline-container, fall back to + the IPD of the nearest ancestor reference-area. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml b/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml new file mode 100644 index 000000000..aa681d134 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml @@ -0,0 +1,61 @@ + + + + + +

+ Checks that percentage values for the dimensions of an fo:inline-container are resolved + properly. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
-- cgit v1.2.3 From 19f04b9ac8fdaaea7436753ef272d19b658d0471 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 18:54:11 +0000 Subject: Properly handle "error-if-overflow" value of "overflow" property when overflow happens in inline-progression-direction git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536486 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/BlockStackingLayoutManager.java | 3 +++ .../inline/InlineContainerLayoutManager.java | 30 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index d11f062cb..250e07727 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -36,6 +36,7 @@ import org.apache.fop.fo.properties.BreakPropertySet; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.SpaceProperty; +import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.ListUtil; @@ -1246,6 +1247,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager public boolean handleOverflow(int milliPoints) { if (getParent() instanceof BlockStackingLayoutManager) { return ((BlockStackingLayoutManager) getParent()).handleOverflow(milliPoints); + } else if (getParent() instanceof InlineContainerLayoutManager) { + return ((InlineContainerLayoutManager) getParent()).handleOverflow(milliPoints); } return false; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 7e2b186be..85d21833b 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -63,6 +63,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private int contentAreaBPD; private List childElements; + private int ipdOverflow; private InlineViewport currentViewport; private Container referenceArea; @@ -119,17 +120,23 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } else { contentAreaBPD = (int) Math.round(bpdValue); if (contentAreaBPD < actualBPD) { - BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( - fobj.getUserAgent().getEventBroadcaster()); - boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + BlockLevelEventProducer eventProducer = getBlockLevelEventProducer(); eventProducer.viewportBPDOverflow(this, fobj.getName(), - actualBPD - contentAreaBPD, needClip(), canRecover, + actualBPD - contentAreaBPD, needClip(), canRecoverFromOverflow(), fobj.getLocator()); } } } } + private BlockLevelEventProducer getBlockLevelEventProducer() { + return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); + } + + private boolean canRecoverFromOverflow() { + return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + } + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { List allChildElements = new LinkedList(); LayoutManager childLM; @@ -141,12 +148,22 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } + handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); // TODO break-before, break-after return allChildElements; } + private void handleIPDOverflow() { + if (ipdOverflow > 0) { + BlockLevelEventProducer eventProducer = getBlockLevelEventProducer(); + eventProducer.viewportIPDOverflow(this, fobj.getName(), + ipdOverflow, needClip(), canRecoverFromOverflow(), + fobj.getLocator()); + } + } + private void wrapPositions(List elements) { for (ListElement element : elements) { Position position = new NonLeafPosition(this, element.getPosition()); @@ -234,6 +251,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen context.getWritingMode()); } + public boolean handleOverflow(int milliPoints) { + ipdOverflow = Math.max(ipdOverflow, milliPoints); + return true; + } + public List addALetterSpaceTo(List oldList) { throw new UnsupportedOperationException("Not implemented"); } -- cgit v1.2.3 From adfb500608ab08eb687b0d8047c9042b6a9a5170 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Mon, 28 Oct 2013 23:33:07 +0000 Subject: FOP-2305: Poor resolution of PCL output in trunk vs. 1.1; target-resolution was being overridden by default value git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1536556 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/cli/CommandLineOptions.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index a216e6b3c..41fcdcac5 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -176,9 +176,6 @@ public class CommandLineOptions { //Factory config is set up, now we can create the user agent foUserAgent = factory.newFOUserAgent(); foUserAgent.getRendererOptions().putAll(renderingOptions); - if (targetResolution != 0) { - foUserAgent.setTargetResolution(targetResolution); - } addXSLTParameter("fop-output-format", getOutputFormat()); addXSLTParameter("fop-version", Version.getVersion()); foUserAgent.setConserveMemoryPolicy(conserveMemoryPolicy); -- cgit v1.2.3 From 51dc03c675a424cb6f59e9634775cf17970564da Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Tue, 29 Oct 2013 22:51:53 +0000 Subject: FOP-2308: text-transform="capitalize" assumes input text is lowercase git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1536924 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/FOText.java | 2 +- test/layoutengine/standard-testcases/wrapper_text-transform_1.xml | 2 +- test/layoutengine/standard-testcases/wrapper_text-transform_2.xml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 2fc998c63..2b707637c 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -297,7 +297,7 @@ public class FOText extends FONode implements CharSequence { */ tmp.put(Character.toTitleCase(c)); } else { - tmp.put(c); + tmp.put(Character.toLowerCase(c)); } break; default: diff --git a/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml b/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml index 886d94c7b..e5607d12c 100644 --- a/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml +++ b/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml @@ -41,7 +41,7 @@ - + diff --git a/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml b/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml index e296ef183..8a0813337 100644 --- a/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml +++ b/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml @@ -42,7 +42,7 @@ - - + + -- cgit v1.2.3 From 2d97c4c3db687e0a914cbe627dda16969a104578 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 30 Oct 2013 08:57:55 +0000 Subject: reverted 1536924 (FOP-2308) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1537012 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/FOText.java | 2 +- test/layoutengine/standard-testcases/wrapper_text-transform_1.xml | 2 +- test/layoutengine/standard-testcases/wrapper_text-transform_2.xml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 2b707637c..2fc998c63 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -297,7 +297,7 @@ public class FOText extends FONode implements CharSequence { */ tmp.put(Character.toTitleCase(c)); } else { - tmp.put(Character.toLowerCase(c)); + tmp.put(c); } break; default: diff --git a/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml b/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml index e5607d12c..886d94c7b 100644 --- a/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml +++ b/test/layoutengine/standard-testcases/wrapper_text-transform_1.xml @@ -41,7 +41,7 @@ - + diff --git a/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml b/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml index 8a0813337..e296ef183 100644 --- a/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml +++ b/test/layoutengine/standard-testcases/wrapper_text-transform_2.xml @@ -42,7 +42,7 @@ - - + + -- cgit v1.2.3 From add7ec865295a648dcaadb464292a872a140aaf8 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Thu, 31 Oct 2013 19:44:25 +0000 Subject: FOP-2310: Fix misplaced table cell border in WM RTL context. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1537600 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/FObj.java | 17 ++++++++++++++++- src/java/org/apache/fop/fo/flow/BlockContainer.java | 18 ++++++++++++++++-- .../org/apache/fop/fo/flow/InlineContainer.java | 16 +++++++++++++++- src/java/org/apache/fop/fo/flow/table/Table.java | 13 ++++++++++++- .../org/apache/fop/fo/pagination/PageSequence.java | 18 +++++++++++++++++- .../apache/fop/layoutmgr/BlockLayoutManager.java | 2 +- .../fop/layoutmgr/list/ListBlockLayoutManager.java | 2 ++ .../list/ListItemContentLayoutManager.java | 1 + .../fop/layoutmgr/list/ListItemLayoutManager.java | 2 ++ .../table/TableAndCaptionLayoutManager.java | 1 + .../layoutmgr/table/TableCaptionLayoutManager.java | 1 + .../fop/layoutmgr/table/TableCellLayoutManager.java | 19 ++++++++++++++----- .../fop/render/pdf/PDFStructureTreeBuilder.java | 7 ++++++- .../apache/fop/render/rtf/RTFPlaceHolderHelper.java | 6 ++++-- .../fop/render/rtf/rtflib/tools/BuilderContext.java | 4 ++-- src/java/org/apache/fop/traits/WritingMode.java | 5 +++-- .../org/apache/fop/traits/WritingModeTraits.java | 21 +++++++++++++++------ .../apache/fop/traits/WritingModeTraitsGetter.java | 5 +++++ .../apache/fop/traits/WritingModeTraitsSetter.java | 5 +++-- status.xml | 3 +++ 20 files changed, 139 insertions(+), 27 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index 92dc9fd73..b6f2ec83e 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -611,7 +611,7 @@ public abstract class FObj extends FONode implements Constants { if (bidiLevel >= 0) { if ((this.bidiLevel < 0) || (bidiLevel < this.bidiLevel)) { this.bidiLevel = bidiLevel; - if (parent != null) { + if ((parent != null) && !isBidiPropagationBoundary()) { FObj foParent = (FObj) parent; int parentBidiLevel = foParent.getBidiLevel(); if ((parentBidiLevel < 0) || (bidiLevel < parentBidiLevel)) { @@ -646,10 +646,25 @@ public abstract class FObj extends FONode implements Constants { return level; } } + if (isBidiInheritanceBoundary()) { + break; + } } return -1; } + protected boolean isBidiBoundary(boolean propagate) { + return false; + } + + private boolean isBidiInheritanceBoundary() { + return isBidiBoundary(false); + } + + private boolean isBidiPropagationBoundary() { + return isBidiBoundary(true); + } + /** * Add a new extension attachment to this FObj. * (see org.apache.fop.fo.FONode for details) diff --git a/src/java/org/apache/fop/fo/flow/BlockContainer.java b/src/java/org/apache/fop/fo/flow/BlockContainer.java index b8616c1ce..b250a117c 100644 --- a/src/java/org/apache/fop/fo/flow/BlockContainer.java +++ b/src/java/org/apache/fop/fo/flow/BlockContainer.java @@ -98,7 +98,8 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); span = pList.get(PR_SPAN).getEnum(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()), + pList.getExplicit(PR_WRITING_MODE) != null); disableColumnBalancing = pList.get(PR_X_DISABLE_COLUMN_BALANCING).getEnum(); } @@ -280,6 +281,14 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod return writingModeTraits.getWritingMode(); } + /** + * Obtain writing mode explicit indicator. + * @return the writing mode explicit indicator + */ + public boolean getExplicitWritingMode() { + return writingModeTraits.getExplicitWritingMode(); + } + /** {@inheritDoc} */ public String getLocalName() { return "block-container"; @@ -292,5 +301,10 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod public int getNameId() { return FO_BLOCK_CONTAINER; } -} + @Override + protected boolean isBidiBoundary(boolean propagate) { + return getExplicitWritingMode(); + } + +} diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index 748eb593a..cf970c325 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -96,7 +96,8 @@ public class InlineContainer extends FObj { overflow = pList.get(PR_OVERFLOW).getEnum(); referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()), + pList.getExplicit(PR_WRITING_MODE) != null); } /** @@ -238,6 +239,14 @@ public class InlineContainer extends FObj { return writingModeTraits.getWritingMode(); } + /** + * Obtain writing mode explicit indicator. + * @return the writing mode explicit indicator + */ + public boolean getExplicitWritingMode() { + return writingModeTraits.getExplicitWritingMode(); + } + /** {@inheritDoc} */ public String getLocalName() { return "inline-container"; @@ -256,4 +265,9 @@ public class InlineContainer extends FObj { return false; } + @Override + protected boolean isBidiBoundary(boolean propagate) { + return getExplicitWritingMode(); + } + } diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java index 983954e79..e1252e45e 100644 --- a/src/java/org/apache/fop/fo/flow/table/Table.java +++ b/src/java/org/apache/fop/fo/flow/table/Table.java @@ -139,7 +139,8 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break tableOmitFooterAtBreak = pList.get(PR_TABLE_OMIT_FOOTER_AT_BREAK).getEnum(); tableOmitHeaderAtBreak = pList.get(PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()), + pList.getExplicit(PR_WRITING_MODE) != null); //Bind extension properties widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength(); @@ -554,6 +555,11 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break return writingModeTraits.getWritingMode(); } + /** {@inheritDoc} */ + public boolean getExplicitWritingMode() { + return writingModeTraits.getExplicitWritingMode(); + } + /** @return the "fox:widow-content-limit" extension FO trait */ public Length getWidowContentLimit() { return widowContentLimit; @@ -620,4 +626,9 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break return ranges; } + @Override + protected boolean isBidiBoundary(boolean propagate) { + return getExplicitWritingMode(); + } + } diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index 368b69f90..ba874d31f 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -96,7 +96,8 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra masterReference = pList.get(PR_MASTER_REFERENCE).getString(); referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()), + pList.getExplicit(PR_WRITING_MODE) != null); if (masterReference == null || masterReference.equals("")) { missingPropertyError("master-reference"); } @@ -403,6 +404,16 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra } } + /** + * {@inheritDoc} + */ + public boolean getExplicitWritingMode() { + if (writingModeTraits != null) { + return writingModeTraits.getExplicitWritingMode(); + } else { + return false; + } + } @Override protected Stack collectDelimitedTextRanges(Stack ranges, DelimitedTextRange currentRange) { @@ -423,6 +434,11 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra return ranges; } + @Override + protected boolean isBidiBoundary(boolean propagate) { + return true; + } + /** * Releases a page-sequence's children after the page-sequence has been fully processed. */ diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 0fb738aea..55b17e953 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -360,7 +360,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co curBlockArea.setIPD(super.getContentAreaIPD()); - curBlockArea.setBidiLevel(getBlockFO().getBidiLevel()); + curBlockArea.setBidiLevel(getBlockFO().getBidiLevelRecursive()); TraitSetter.addBreaks(curBlockArea, getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter()); diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 61d8a891d..5d89b3440 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -242,6 +242,8 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager int contentIPD = referenceIPD - getIPIndents(); curBlockArea.setIPD(contentIPD); + curBlockArea.setBidiLevel(getListBlockFO().getBidiLevel()); + setCurrentArea(curBlockArea); } return curBlockArea; diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java index f017da381..b16c9dfc4 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java @@ -181,6 +181,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp //TODO: Check - itemIPD never set? curBlockArea.setIPD(itemIPD); //curBlockArea.setHeight(); + curBlockArea.setBidiLevel(getPartFO().getBidiLevel()); TraitSetter.setProducerID(curBlockArea, getPartFO().getId()); diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 083e4ee1b..84dba03ee 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -615,6 +615,8 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements int contentIPD = referenceIPD - getIPIndents(); curBlockArea.setIPD(contentIPD); + curBlockArea.setBidiLevel(fo.getBidiLevel()); + setCurrentArea(curBlockArea); } return curBlockArea; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java index 0582a0283..aaa896ce3 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java @@ -184,6 +184,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager { Area parentArea = parentLayoutManager.getParentArea(curBlockArea); int referenceIPD = parentArea.getIPD(); curBlockArea.setIPD(referenceIPD); + curBlockArea.setBidiLevel(getTableAndCaptionFO().getBidiLevel()); // Get reference IPD from parentArea setCurrentArea(curBlockArea); // ??? for generic operations } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java index 66f7ad9f2..8823c0fae 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java @@ -180,6 +180,7 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager { Area parentArea = parentLayoutManager.getParentArea(curBlockArea); int referenceIPD = parentArea.getIPD(); curBlockArea.setIPD(referenceIPD); + curBlockArea.setBidiLevel(getTableCaptionFO().getBidiLevel()); // Get reference IPD from parentArea setCurrentArea(curBlockArea); // ??? for generic operations } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index c8f2cea85..af8e071d5 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -441,12 +441,14 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager Block[][] blocks = new Block[getTableCell().getNumberRowsSpanned()][getTableCell() .getNumberColumnsSpanned()]; GridUnit[] gridUnits = (GridUnit[]) primaryGridUnit.getRows().get(startRow); + int level = getTableCell().getBidiLevelRecursive(); for (int x = 0; x < getTableCell().getNumberColumnsSpanned(); x++) { GridUnit gu = gridUnits[x]; BorderInfo border = gu.getBorderBefore(borderBeforeWhich); int borderWidth = border.getRetainedWidth() / 2; if (borderWidth > 0) { - addBorder(blocks, startRow, x, Trait.BORDER_BEFORE, border, firstOnPage); + addBorder(blocks, startRow, x, Trait.BORDER_BEFORE, border, + firstOnPage, level); adjustYOffset(blocks[startRow][x], -borderWidth); adjustBPD(blocks[startRow][x], -borderWidth); } @@ -457,7 +459,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager BorderInfo border = gu.getBorderAfter(borderAfterWhich); int borderWidth = border.getRetainedWidth() / 2; if (borderWidth > 0) { - addBorder(blocks, endRow, x, Trait.BORDER_AFTER, border, lastOnPage); + addBorder(blocks, endRow, x, Trait.BORDER_AFTER, border, + lastOnPage, level); adjustBPD(blocks[endRow][x], -borderWidth); } } @@ -466,7 +469,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager BorderInfo border = gridUnits[0].getBorderStart(); int borderWidth = border.getRetainedWidth() / 2; if (borderWidth > 0) { - addBorder(blocks, y, 0, Trait.BORDER_START, border, inFirstColumn); + addBorder(blocks, y, 0, Trait.BORDER_START, border, + inFirstColumn, level); adjustXOffset(blocks[y][0], borderWidth); adjustIPD(blocks[y][0], -borderWidth); } @@ -474,7 +478,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager borderWidth = border.getRetainedWidth() / 2; if (borderWidth > 0) { addBorder(blocks, y, gridUnits.length - 1, Trait.BORDER_END, border, - inLastColumn); + inLastColumn, level); adjustIPD(blocks[y][gridUnits.length - 1], -borderWidth); } } @@ -511,10 +515,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager if (getTableCell().getDisplayAlign() == EN_CENTER) { Block space = new Block(); space.setBPD((cellBPD - usedBPD) / 2); + space.setBidiLevel(getTableCell().getBidiLevelRecursive()); curBlockArea.addBlock(space); } else if (getTableCell().getDisplayAlign() == EN_AFTER) { Block space = new Block(); space.setBPD(cellBPD - usedBPD); + space.setBidiLevel(getTableCell().getBidiLevelRecursive()); curBlockArea.addBlock(space); } } @@ -590,11 +596,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager } private void addBorder(Block[][] blocks, int i, int j, Integer side, BorderInfo border, - boolean outer) { + boolean outer, int level) { if (blocks[i][j] == null) { blocks[i][j] = new Block(); blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); blocks[i][j].setPositioning(Block.ABSOLUTE); + blocks[i][j].setBidiLevel(level); } blocks[i][j].addTrait(side, BorderProps.makeRectangular(border.getStyle(), border.getRetainedWidth(), border.getColor(), @@ -629,6 +636,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager block.setBPD(bpd); block.setXOffset(xoffset + startIndent - paddingStart); block.setYOffset(yoffset + borderBeforeWidth); + block.setBidiLevel(getTableCell().getBidiLevelRecursive()); return block; } @@ -654,6 +662,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager curBlockArea.setXOffset(xoffset + startIndent); curBlockArea.setYOffset(yoffset); curBlockArea.setIPD(cellIPD); + curBlockArea.setBidiLevel(getTableCell().getBidiLevelRecursive()); /*Area parentArea =*/ parentLayoutManager.getParentArea(curBlockArea); // Get reference IPD from parentArea diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 4d34b8be4..3cc7a1017 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -358,7 +358,12 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { } public StructureTreeElement startNode(String name, Attributes attributes, StructureTreeElement parent) { - PDFStructElem parentElem = parent == null ? ancestors.getFirst() : (PDFStructElem) parent; + PDFStructElem parentElem; + if ((parent != null) && (parent instanceof PDFStructElem)) { + parentElem = (PDFStructElem) parent; + } else { + parentElem = ancestors.getFirst(); + } PDFStructElem structElem = createStructureElement(name, parentElem, attributes, pdfFactory, eventBroadcaster); ancestors.addFirst(structElem); diff --git a/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java index c31c68db1..e3278ad95 100644 --- a/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java +++ b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java @@ -65,8 +65,10 @@ public class RTFPlaceHolderHelper { builderContext.pushContainer(newRow); builderContext.getTableContext().selectFirstColumn(); } - } catch (Exception ex) { - throw new RtfException(ex.getMessage()); + } catch (org.apache.fop.apps.FOPException e) { + throw new RtfException(e.getMessage()); + } catch (java.io.IOException e) { + throw new RtfException(e.getMessage()); } } } diff --git a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java index 6dcae5412..4b3f8bd4a 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java @@ -196,8 +196,8 @@ public class BuilderContext { } else { /* This should never happen unless a placeholder is not catered for * in the RTFHandler.endContainer method. */ - LOG.warn("Unhandled RTF structure tag mismatch detected between " + - aClass.getSimpleName() + " and "+object.getClass().getSimpleName()); + LOG.warn("Unhandled RTF structure tag mismatch detected between " + + aClass.getSimpleName() + " and " + object.getClass().getSimpleName()); } } diff --git a/src/java/org/apache/fop/traits/WritingMode.java b/src/java/org/apache/fop/traits/WritingMode.java index 8718118ef..f8c784941 100644 --- a/src/java/org/apache/fop/traits/WritingMode.java +++ b/src/java/org/apache/fop/traits/WritingMode.java @@ -54,8 +54,9 @@ public final class WritingMode extends TraitEnum { * Assign writing mode traits from this trait to the specified * writing mode traits setter. * @param wms a writing mode traits setter + * @param explicit true if writing mode property explicitly specified */ - public void assignWritingModeTraits(WritingModeTraitsSetter wms) { + public void assignWritingModeTraits(WritingModeTraitsSetter wms, boolean explicit) { Direction inlineProgressionDirection; Direction blockProgressionDirection; Direction columnProgressionDirection; @@ -97,7 +98,7 @@ public final class WritingMode extends TraitEnum { wms.setColumnProgressionDirection(columnProgressionDirection); wms.setRowProgressionDirection(rowProgressionDirection); wms.setShiftDirection(shiftDirection); - wms.setWritingMode(this); + wms.setWritingMode(this, explicit); } /** diff --git a/src/java/org/apache/fop/traits/WritingModeTraits.java b/src/java/org/apache/fop/traits/WritingModeTraits.java index 279326afa..fbd150ca9 100644 --- a/src/java/org/apache/fop/traits/WritingModeTraits.java +++ b/src/java/org/apache/fop/traits/WritingModeTraits.java @@ -31,20 +31,21 @@ public class WritingModeTraits implements WritingModeTraitsSetter { private Direction rowProgressionDirection; private Direction shiftDirection; private WritingMode writingMode; + private boolean explicit; /** * Default writing mode traits constructor. */ public WritingModeTraits() { - this (WritingMode.LR_TB); + this (WritingMode.LR_TB, false); } /** * Construct writing mode traits using the specified writing mode. * @param writingMode a writing mode traits object */ - public WritingModeTraits(WritingMode writingMode) { - assignWritingModeTraits(writingMode); + public WritingModeTraits(WritingMode writingMode, boolean explicit) { + assignWritingModeTraits(writingMode, explicit); } /** @@ -124,18 +125,26 @@ public class WritingModeTraits implements WritingModeTraitsSetter { return writingMode; } + /** + * @return the "explicit-writing-mode" trait. + */ + public boolean getExplicitWritingMode() { + return explicit; + } + /** * @param writingMode the "writing-mode" trait. */ - public void setWritingMode(WritingMode writingMode) { + public void setWritingMode(WritingMode writingMode, boolean explicit) { this.writingMode = writingMode; + this.explicit = explicit; } /** * @param writingMode the "writing-mode" trait. */ - public void assignWritingModeTraits(WritingMode writingMode) { - writingMode.assignWritingModeTraits(this); + public void assignWritingModeTraits(WritingMode writingMode, boolean explicit) { + writingMode.assignWritingModeTraits(this, explicit); } /** diff --git a/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java b/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java index a67e437c9..7340b3e8c 100644 --- a/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java +++ b/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java @@ -55,4 +55,9 @@ public interface WritingModeTraitsGetter { */ WritingMode getWritingMode(); + /** + * @return the "explicit-writing-mode" trait + */ + boolean getExplicitWritingMode(); + } diff --git a/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java b/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java index 6dcf0fb12..b70f73954 100644 --- a/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java +++ b/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java @@ -58,13 +58,14 @@ public interface WritingModeTraitsSetter extends WritingModeTraitsGetter { * Set value of writing-mode trait. * @param writingMode the "writing-mode" trait */ - void setWritingMode(WritingMode writingMode); + void setWritingMode(WritingMode writingMode, boolean explicit); /** * Collectivelly assign values to all writing mode traits based upon a specific * writing mode. * @param writingMode the "writing-mode" trait + * @param explicit true if writing mode explicitly specified */ - void assignWritingModeTraits(WritingMode writingMode); + void assignWritingModeTraits(WritingMode writingMode, boolean explicit); } diff --git a/status.xml b/status.xml index 5f29deefb..352f487c3 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Fix misplaced table cell border in WM RTL context. + Enable support for PDF page transitions. -- cgit v1.2.3 From 69f539c6f5fb7ef4b1ade45ad110901253772892 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 31 Oct 2013 20:06:34 +0000 Subject: Take into account the inline-container's bpd in the alignment context git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1537612 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 85d21833b..669b88838 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -247,8 +247,10 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen FontInfo fi = fobj.getFOEventHandler().getFontInfo(); FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(fs, ic.getLineHeight().getOptimum(this).getLength().getValue(this), // TODO - context.getWritingMode()); + return new AlignmentContext(contentAreaBPD, + ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), + ic.getBaselineShift(), ic.getDominantBaseline(), + context.getAlignmentContext()); } public boolean handleOverflow(int milliPoints) { -- cgit v1.2.3 From b0c8e3a541682a9a2becd5438e21d585d2f1656d Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 31 Oct 2013 20:07:19 +0000 Subject: Re-ordered methods from higher to lower level git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1537614 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 70 +++++++++++----------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 669b88838..75eb12d5b 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -107,6 +107,24 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { + List allChildElements = new LinkedList(); + LayoutManager childLM; + while ((childLM = getChildLM()) != null) { + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? + childLC.setRefIPD(layoutContext.getRefIPD()); + @SuppressWarnings("unchecked") + List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + allChildElements.addAll(childElements); + // TODO breaks, keeps, empty content + } + handleIPDOverflow(); + wrapPositions(allChildElements); + SpaceResolver.resolveElementList(allChildElements); + // TODO break-before, break-after + return allChildElements; + } + private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); @@ -129,30 +147,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - private BlockLevelEventProducer getBlockLevelEventProducer() { - return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); - } - - private boolean canRecoverFromOverflow() { - return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; - } - - private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { - List allChildElements = new LinkedList(); - LayoutManager childLM; - while ((childLM = getChildLM()) != null) { - LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? - childLC.setRefIPD(layoutContext.getRefIPD()); - @SuppressWarnings("unchecked") - List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); - allChildElements.addAll(childElements); - // TODO breaks, keeps, empty content - } - handleIPDOverflow(); - wrapPositions(allChildElements); - SpaceResolver.resolveElementList(allChildElements); - // TODO break-before, break-after - return allChildElements; + protected AlignmentContext makeAlignmentContext(LayoutContext context) { + InlineContainer ic = (InlineContainer) fobj; + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); + Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); + return new AlignmentContext(contentAreaBPD, + ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), + ic.getBaselineShift(), ic.getDominantBaseline(), + context.getAlignmentContext()); } private void handleIPDOverflow() { @@ -172,6 +175,14 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } + private BlockLevelEventProducer getBlockLevelEventProducer() { + return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); + } + + private boolean canRecoverFromOverflow() { + return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -242,17 +253,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); } - protected AlignmentContext makeAlignmentContext(LayoutContext context) { - InlineContainer ic = (InlineContainer) fobj; - FontInfo fi = fobj.getFOEventHandler().getFontInfo(); - FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); - Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(contentAreaBPD, - ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), - ic.getBaselineShift(), ic.getDominantBaseline(), - context.getAlignmentContext()); - } - public boolean handleOverflow(int milliPoints) { ipdOverflow = Math.max(ipdOverflow, milliPoints); return true; -- cgit v1.2.3 From 8841c3bdd571ff02ff4e99dbce355b78127e4190 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Fri, 1 Nov 2013 14:34:18 +0000 Subject: FOP-2301: Enable support for PDF sub-page transitions. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1537948 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop-intermediate-format-ng-content.xsd | 1 + src/java/org/apache/fop/area/AreaTreeParser.java | 2 +- src/java/org/apache/fop/area/Trait.java | 5 +- src/java/org/apache/fop/fo/Constants.java | 5 +- src/java/org/apache/fop/fo/FOPropertyMapping.java | 7 + src/java/org/apache/fop/fo/FObj.java | 14 +- .../fop/fo/extensions/ExtensionElementMapping.java | 2 + .../fop/layoutmgr/BlockContainerLayoutManager.java | 1 + .../apache/fop/layoutmgr/BlockLayoutManager.java | 1 + src/java/org/apache/fop/layoutmgr/TraitSetter.java | 11 + .../fop/layoutmgr/inline/InlineLayoutManager.java | 1 + src/java/org/apache/fop/pdf/AbstractPDFStream.java | 4 +- src/java/org/apache/fop/pdf/PDFDictionary.java | 2 +- src/java/org/apache/fop/pdf/PDFDocument.java | 54 ++++- src/java/org/apache/fop/pdf/PDFFactory.java | 24 ++ .../apache/fop/pdf/PDFIdentifiedDictionary.java | 42 ++++ src/java/org/apache/fop/pdf/PDFLayer.java | 86 +++++++ src/java/org/apache/fop/pdf/PDFNavigator.java | 93 ++++++++ .../org/apache/fop/pdf/PDFNavigatorAction.java | 28 +++ src/java/org/apache/fop/pdf/PDFPaintingState.java | 34 ++- src/java/org/apache/fop/pdf/PDFResources.java | 34 ++- .../org/apache/fop/pdf/PDFSetOCGStateAction.java | 82 +++++++ .../org/apache/fop/pdf/PDFTransitionAction.java | 79 +++++++ .../org/apache/fop/render/AbstractRenderer.java | 58 +++++ src/java/org/apache/fop/render/afp/AFPPainter.java | 2 +- .../fop/render/intermediate/AbstractIFPainter.java | 4 +- .../fop/render/intermediate/IFGraphicContext.java | 23 +- .../apache/fop/render/intermediate/IFPainter.java | 8 +- .../apache/fop/render/intermediate/IFParser.java | 5 +- .../apache/fop/render/intermediate/IFRenderer.java | 32 ++- .../fop/render/intermediate/IFSerializer.java | 13 +- .../apache/fop/render/java2d/Java2DPainter.java | 2 +- .../apache/fop/render/java2d/Java2DRenderer.java | 8 + src/java/org/apache/fop/render/pcl/PCLPainter.java | 2 +- .../apache/fop/render/pdf/PDFContentGenerator.java | 81 +++++-- .../apache/fop/render/pdf/PDFDocumentHandler.java | 3 +- src/java/org/apache/fop/render/pdf/PDFPainter.java | 4 +- .../apache/fop/render/pdf/PDFRenderingUtil.java | 261 ++++++++++++++++++++- .../extensions/AbstractPDFDictionaryElement.java | 43 ---- .../render/pdf/extensions/PDFActionElement.java | 64 +++++ .../render/pdf/extensions/PDFActionExtension.java | 32 +++ .../fop/render/pdf/extensions/PDFArrayElement.java | 82 +++++++ .../render/pdf/extensions/PDFArrayExtension.java | 84 +++++++ .../render/pdf/extensions/PDFCatalogElement.java | 45 ++++ .../render/pdf/extensions/PDFCatalogExtension.java | 29 +++ .../pdf/extensions/PDFCollectionEntryElement.java | 146 ++++++++++++ .../extensions/PDFCollectionEntryExtension.java | 43 ++++ .../pdf/extensions/PDFCollectionExtension.java | 34 +++ .../pdf/extensions/PDFDictionaryAttachment.java | 70 ++++-- .../pdf/extensions/PDFDictionaryElement.java | 83 ++++--- .../pdf/extensions/PDFDictionaryEntryElement.java | 109 --------- .../extensions/PDFDictionaryEntryExtension.java | 110 --------- .../pdf/extensions/PDFDictionaryEntryType.java | 56 ----- .../pdf/extensions/PDFDictionaryExtension.java | 97 ++++---- .../render/pdf/extensions/PDFDictionaryType.java | 19 +- .../render/pdf/extensions/PDFElementMapping.java | 90 +++++-- .../render/pdf/extensions/PDFExtensionHandler.java | 124 +++++++--- .../fop/render/pdf/extensions/PDFLayerElement.java | 45 ++++ .../render/pdf/extensions/PDFLayerExtension.java | 29 +++ .../render/pdf/extensions/PDFNavigatorElement.java | 45 ++++ .../pdf/extensions/PDFNavigatorExtension.java | 30 +++ .../render/pdf/extensions/PDFObjectExtension.java | 101 ++++++++ .../fop/render/pdf/extensions/PDFObjectType.java | 60 +++++ .../fop/render/pdf/extensions/PDFPageElement.java | 64 +++++ .../render/pdf/extensions/PDFPageExtension.java | 73 ++++++ .../render/pdf/extensions/PDFReferenceElement.java | 58 +++++ .../pdf/extensions/PDFReferenceExtension.java | 61 +++++ src/java/org/apache/fop/render/ps/PSPainter.java | 2 +- .../org/apache/fop/render/txt/TXTRenderer.java | 8 + .../org/apache/fop/render/xml/XMLRenderer.java | 10 + .../org/apache/fop/util/AbstractPaintingState.java | 67 ++++-- .../org/apache/fop/render/svg/SVGPainter.java | 10 +- status.xml | 5 +- .../apache/fop/fonts/EmbedFontInfoTestCase.java | 2 + test/java/org/apache/fop/fonts/FontsTestSuite.java | 2 +- test/java/org/apache/fop/pdf/PDFDestsTestCase.java | 2 +- .../org/apache/fop/pdf/PDFDictionaryTestCase.java | 2 +- .../org/apache/fop/pdf/PDFPageLabelsTestCase.java | 28 +-- .../java/org/apache/fop/pdf/PDFStreamTestCase.java | 2 +- .../fop/pdf/xref/CrossReferenceTableTestCase.java | 2 +- .../intermediate/AbstractIFPainterTestCase.java | 2 +- .../pdf-dictionary-extension_2.xml | 74 ++++++ .../pdf-dictionary-extension_3.xml | 122 ++++++++++ 83 files changed, 2796 insertions(+), 588 deletions(-) create mode 100644 src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java create mode 100644 src/java/org/apache/fop/pdf/PDFLayer.java create mode 100644 src/java/org/apache/fop/pdf/PDFNavigator.java create mode 100644 src/java/org/apache/fop/pdf/PDFNavigatorAction.java create mode 100644 src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java create mode 100644 src/java/org/apache/fop/pdf/PDFTransitionAction.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml (limited to 'src/java/org/apache/fop') diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd index d6f0c694c..2e58a7f45 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd @@ -38,6 +38,7 @@ + diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index c79f975c1..f6de99dd0 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -1040,7 +1040,7 @@ public class AreaTreeParser { } private static final Object[] SUBSET_COMMON = new Object[] { - Trait.PROD_ID}; + Trait.PROD_ID, Trait.LAYER}; private static final Object[] SUBSET_LINK = new Object[] { Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK}; private static final Object[] SUBSET_COLOR = new Object[] { diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index cd0d4becf..eac9d440d 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -169,9 +169,11 @@ public final class Trait implements Serializable { /** shift direction trait */ public static final Integer SHIFT_DIRECTION = 42; + /** For optional content groups. */ + public static final Integer LAYER = 43; /** Maximum value used by trait keys */ - public static final int MAX_TRAIT_KEY = 42; + public static final int MAX_TRAIT_KEY = 43; private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1]; @@ -243,6 +245,7 @@ public final class Trait implements Serializable { new TraitInfo("block-progression-direction", Direction.class)); put(SHIFT_DIRECTION, new TraitInfo("shift-direction", Direction.class)); + put(LAYER, new TraitInfo("layer", String.class)); } diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 28f02a762..086cbca40 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -816,8 +816,11 @@ public interface Constants { /** Scope for table header */ int PR_X_HEADER_COLUMN = 290; + /** For specifying PDF optional content group (layer) binding. */ + int PR_X_LAYER = 291; + /** Number of property constants defined */ - int PROPERTY_COUNT = 290; + int PROPERTY_COUNT = 291; // compound property constants diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 36e3f21c4..cc4fe9e2c 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2721,6 +2721,13 @@ public final class FOPropertyMapping implements Constants { m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); m.setDefault("auto"); addPropertyMaker("z-index", m); + + // fox:layer + m = new StringProperty.Maker(PR_X_LAYER); + m.setInherited(false); + m.setDefault(""); + addPropertyMaker("fox:layer", m); + } private void createShorthandProperties() { diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index b6f2ec83e..0d1a2a84b 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -74,7 +74,8 @@ public abstract class FObj extends FONode implements Constants { private int bidiLevel = -1; // The value of properties relevant for all fo objects - private String id = null; + private String id; + private String layer; // End of property values /** @@ -173,6 +174,7 @@ public abstract class FObj extends FONode implements Constants { */ public void bind(PropertyList pList) throws FOPException { id = pList.get(PR_ID).getString(); + layer = pList.get(PR_X_LAYER).getString(); } /** @@ -583,6 +585,16 @@ public abstract class FObj extends FONode implements Constants { return (id != null && id.length() > 0); } + /** @return the "layer" property. */ + public String getLayer() { + return layer; + } + + /** @return whether this object has an layer set */ + public boolean hasLayer() { + return (layer != null && layer.length() > 0); + } + /** {@inheritDoc} */ public String getNamespaceURI() { return FOElementMapping.URI; diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 09b47f02a..82db43e59 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -66,6 +66,8 @@ public class ExtensionElementMapping extends ElementMapping { PROPERTY_ATTRIBUTES.add("border-before-end-radius"); PROPERTY_ATTRIBUTES.add("border-after-start-radius"); PROPERTY_ATTRIBUTES.add("border-after-end-radius"); + //Optional content groups (layers) + PROPERTY_ATTRIBUTES.add("layer"); } /** diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index a23cd28f1..eced48939 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -867,6 +867,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl transferForeignAttributes(viewportBlockArea); TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId()); + TraitSetter.setLayer(viewportBlockArea, getBlockContainerFO().getLayer()); TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), discardBorderBefore, discardBorderAfter, false, false, this); diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 55b17e953..b29b95cb1 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -381,6 +381,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co getBlockFO().getCommonBorderPaddingBackground(), startIndent, endIndent, this); + TraitSetter.setLayer(curBlockArea, getBlockFO().getLayer()); curBlockArea.setLocale(getBlockFO().getCommonHyphenation().getLocale()); curBlockArea.setLocation(FONode.getLocatorString(getBlockFO().getLocator())); diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 739d535ca..af40f0681 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -617,4 +617,15 @@ public final class TraitSetter { area.addTrait(Trait.PROD_ID, id); } } + + /** + * Sets the optional content group layer as a trait on the area. + * @param area the area to set the traits on + * @param layer the layer ID to set + */ + public static void setLayer(Area area, String layer) { + if (layer != null && layer.length() > 0) { + area.addTrait(Trait.LAYER, layer); + } + } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index dd80db1d1..61dcf45c9 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -213,6 +213,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { } if (fobj instanceof Inline || fobj instanceof BasicLink) { TraitSetter.setProducerID(area, fobj.getId()); + TraitSetter.setLayer(area, fobj.getLayer()); } return area; } diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java index 41eed4885..13bd1bda1 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -143,7 +143,7 @@ public abstract class AbstractPDFStream extends PDFObject { */ protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException { int length = 0; - byte[] p = encode("stream\n"); + byte[] p = encode("\nstream\n"); out.write(p); length += p.length; @@ -186,7 +186,7 @@ public abstract class AbstractPDFStream extends PDFObject { throws IOException { int bytesWritten = 0; //Stream header - byte[] buf = encode("stream\n"); + byte[] buf = encode("\nstream\n"); out.write(buf); bytesWritten += buf.length; diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java index 6bacd31a3..ae0b950fd 100644 --- a/src/java/org/apache/fop/pdf/PDFDictionary.java +++ b/src/java/org/apache/fop/pdf/PDFDictionary.java @@ -131,7 +131,7 @@ public class PDFDictionary extends PDFObject { } else { textBuffer.append('\n'); } - textBuffer.append(">>\n"); + textBuffer.append(">>"); } } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index ff9f61201..bcd54fcb9 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -155,6 +155,12 @@ public class PDFDocument { private List launches = new ArrayList(); + private List layers; + + private List navigators; + + private List navigatorActions; + private PDFFactory factory; private FileIDGenerator fileIDGenerator; @@ -477,6 +483,24 @@ public class PDFDocument { if (obj instanceof PDFGoToRemote) { this.gotoremotes.add((PDFGoToRemote) obj); } + if (obj instanceof PDFLayer) { + if (this.layers == null) { + this.layers = new ArrayList(); + } + this.layers.add((PDFLayer) obj); + } + if (obj instanceof PDFNavigator) { + if (this.navigators == null) { + this.navigators = new ArrayList(); + } + this.navigators.add((PDFNavigator) obj); + } + if (obj instanceof PDFNavigatorAction) { + if (this.navigatorActions == null) { + this.navigatorActions = new ArrayList(); + } + this.navigatorActions.add((PDFNavigatorAction) obj); + } } /** @@ -889,6 +913,34 @@ public class PDFDocument { this.accessibilityEnabled = enableAccessibility; } + /** + * + */ + public PDFReference resolveExtensionReference(String id) { + if (layers != null) { + for (PDFLayer layer : layers) { + if (layer.hasId(id)) { + return layer.makeReference(); + } + } + } + if (navigators != null) { + for (PDFNavigator navigator : navigators) { + if (navigator.hasId(id)) { + return navigator.makeReference(); + } + } + } + if (navigatorActions != null) { + for (PDFNavigatorAction action : navigatorActions) { + if (action.hasId(id)) { + return action.makeReference(); + } + } + } + return null; + } + /** * Writes out the entire document * @@ -1009,7 +1061,7 @@ public class PDFDocument { streamIndirectObjects(trailerObjects, stream); TrailerDictionary trailerDictionary = createTrailerDictionary(); long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary); - String trailer = "startxref\n" + startxref + "\n%%EOF\n"; + String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n"; stream.write(encode(trailer)); } diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 631499af1..5af33866e 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -1821,4 +1821,28 @@ public class PDFFactory { return obj; } + public PDFLayer makeLayer(String id) { + PDFLayer layer = new PDFLayer(id); + getDocument().registerObject(layer); + return layer; + } + + public PDFSetOCGStateAction makeSetOCGStateAction(String id) { + PDFSetOCGStateAction action = new PDFSetOCGStateAction(id); + getDocument().registerObject(action); + return action; + } + + public PDFTransitionAction makeTransitionAction(String id) { + PDFTransitionAction action = new PDFTransitionAction(id); + getDocument().registerObject(action); + return action; + } + + public PDFNavigator makeNavigator(String id) { + PDFNavigator navigator = new PDFNavigator(id); + getDocument().registerObject(navigator); + return navigator; + } + } diff --git a/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java new file mode 100644 index 000000000..c2d033aec --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +/** + * Identified Dictionary. + */ +public class PDFIdentifiedDictionary extends PDFDictionary { + + private final String id; + + public PDFIdentifiedDictionary(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public boolean hasId(String id) { + return (this.id != null) && (id != null) && this.id.equals(id); + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFLayer.java b/src/java/org/apache/fop/pdf/PDFLayer.java new file mode 100644 index 000000000..f8f434e87 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFLayer.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Optional Content Group Dictionary, which we will call a 'layer'. + */ +public class PDFLayer extends PDFIdentifiedDictionary { + + public abstract static class Resolver { + private boolean resolved; + private PDFLayer layer; + private Object extension; + public Resolver(PDFLayer layer, Object extension) { + this.layer = layer; + this.extension = extension; + } + public PDFLayer getLayer() { + return layer; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFLayer(String id) { + super(id); + put("Type", new PDFName("OCG")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object name, Object intent, Object usage) { + if (name != null) { + put("Name", name); + } + if (intent != null) { + put("Intent", intent); + } + if (usage != null) { + put("Usage", usage); + } + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFNavigator.java b/src/java/org/apache/fop/pdf/PDFNavigator.java new file mode 100644 index 000000000..fdb97469b --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNavigator.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Navigation Node Dictionary, which we call a 'navigator'. + * This class is used to for sub-page navigation. + */ +public class PDFNavigator extends PDFIdentifiedDictionary { + + public abstract static class Resolver { + private boolean resolved; + private PDFNavigator navigator; + private Object extension; + public Resolver(PDFNavigator navigator, Object extension) { + this.navigator = navigator; + this.extension = extension; + } + public PDFNavigator getNavigator() { + return navigator; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFNavigator(String id) { + super(id); + put("Type", new PDFName("NavNode")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object nextAction, Object nextNode, Object prevAction, Object prevNode, Object duration) { + if (nextAction != null) { + put("NA", nextAction); + } + if (nextNode != null) { + put("Next", nextNode); + } + if (prevAction != null) { + put("PA", prevAction); + } + if (prevNode != null) { + put("Prev", prevNode); + } + if (duration != null) { + put("Dur", duration); + } + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFNavigatorAction.java b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java new file mode 100644 index 000000000..ba32269b5 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +public abstract class PDFNavigatorAction extends PDFIdentifiedDictionary { + + protected PDFNavigatorAction(String id) { + super(id); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFPaintingState.java b/src/java/org/apache/fop/pdf/PDFPaintingState.java index 29d022f61..f6528a30c 100644 --- a/src/java/org/apache/fop/pdf/PDFPaintingState.java +++ b/src/java/org/apache/fop/pdf/PDFPaintingState.java @@ -44,8 +44,6 @@ import org.apache.fop.util.AbstractPaintingState; * previous state then the necessary values can be overridden. * The current transform behaves differently to other values as the * matrix is combined with the current resolved value. - * It is impossible to optimise the result without analysing the all - * the possible combinations after completing. */ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState { @@ -187,6 +185,36 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState return newState; } + public void setLayer(String layer) { + getPDFData().setLayer(layer); + } + + public String getLayer() { + return getPDFData().getLayer(); + } + + public boolean getLayerChanged() { + String layerCurrent = getLayer(); + if (layerCurrent == null) { + return false; + } else if (getStateStack().isEmpty()) { + return true; + } else { + for (int i = getStackLevel(); i > 0; --i) { + String layerPrev = ((PDFData) getStateStack().get(i - 1)).getLayer(); + if (layerPrev == null) { + continue; + } else { + // Both current and prior are set, so, if same, then we know layer + // didn't change (and can stop search), otherwise it did change. + return !layerCurrent.equals(layerPrev); + } + } + // Current layer set, but no prior saved layer set, so must have changed. + return true; + } + } + /** {@inheritDoc} */ @Override protected AbstractData instantiateData() { @@ -209,7 +237,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState AbstractData data = getData(); AbstractData copy = (AbstractData)data.clone(); data.clearTransform(); - getStateStack().add(copy); + getStateStack().push(copy); } private PDFData getPDFData() { diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java index cded7c00a..6d09d5738 100644 --- a/src/java/org/apache/fop/pdf/PDFResources.java +++ b/src/java/org/apache/fop/pdf/PDFResources.java @@ -37,8 +37,8 @@ import org.apache.fop.fonts.base14.ZapfDingbats; /** * Class representing a /Resources object. * - * /Resources object contain a list of references to the fonts for the - * document + * /Resources object contain a list of references to the fonts, patterns, + * shadings, etc., for the document. */ public class PDFResources extends PDFDictionary { @@ -73,6 +73,9 @@ public class PDFResources extends PDFDictionary { /** Map of ICC color spaces (key: ICC profile description) */ protected Map iccColorSpaces = new LinkedHashMap(); + /** Named properties */ + protected Map properties = new LinkedHashMap(); + /** * create a /Resources object. * @@ -191,6 +194,25 @@ public class PDFResources extends PDFDictionary { return cs; } + /** + * Add a named property. + * + * @param name name of property + * @param property reference to property value + */ + public void addProperty(String name, PDFReference property) { + this.properties.put(name, property); + } + + /** + * Get a named property. + * + * @param name name of property + */ + public PDFReference getProperty(String name) { + return this.properties.get(name); + } + @Override public int output(OutputStream stream) throws IOException { populateDictionary(); @@ -253,6 +275,14 @@ public class PDFResources extends PDFDictionary { } put("ColorSpace", dict); } + + if (!properties.isEmpty()) { + PDFDictionary dict = new PDFDictionary(this); + for (String name : properties.keySet()) { + dict.put(name, properties.get(name)); + } + put("Properties", dict); + } } } diff --git a/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java new file mode 100644 index 000000000..a47c5cd59 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +public class PDFSetOCGStateAction extends PDFNavigatorAction { + + public abstract static class Resolver { + private boolean resolved; + private PDFSetOCGStateAction action; + private Object extension; + public Resolver(PDFSetOCGStateAction action, Object extension) { + this.action = action; + this.extension = extension; + } + public PDFSetOCGStateAction getAction() { + return action; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFSetOCGStateAction(String id) { + super(id); + put("Type", new PDFName("Action")); + put("S", new PDFName("SetOCGState")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object state, Object preserveRB, Object nextAction) { + if (state != null) { + put("State", state); + } + if (preserveRB != null) { + put("PreserveRB", preserveRB); + } + if (nextAction != null) { + put("Next", nextAction); + } + } +} diff --git a/src/java/org/apache/fop/pdf/PDFTransitionAction.java b/src/java/org/apache/fop/pdf/PDFTransitionAction.java new file mode 100644 index 000000000..01f8fcf21 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFTransitionAction.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +public class PDFTransitionAction extends PDFNavigatorAction { + + public abstract static class Resolver { + private boolean resolved; + private PDFTransitionAction action; + private Object extension; + public Resolver(PDFTransitionAction action, Object extension) { + this.action = action; + this.extension = extension; + } + public PDFTransitionAction getAction() { + return action; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFTransitionAction(String id) { + super(id); + put("Type", new PDFName("Action")); + put("S", new PDFName("Trans")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object transition, Object nextAction) { + if (transition != null) { + put("Trans", transition); + } + if (nextAction != null) { + put("Next", nextAction); + } + } +} diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index e274e5c4b..9a94e0cc6 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -28,6 +28,7 @@ import java.io.OutputStream; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.Stack; import org.w3c.dom.Document; @@ -112,8 +113,12 @@ public abstract class AbstractRenderer /** the currently active PageViewport */ protected PageViewport currentPageViewport; + /* warned XML handlers */ private Set warnedXMLHandlers; + /* layers stack */ + private Stack layers; + /** {@inheritDoc} */ public abstract void setupFontInfo(FontInfo fontInfo) throws FOPException; @@ -471,6 +476,10 @@ public abstract class AbstractRenderer * @param children The children to render within the block viewport */ protected void renderBlockViewport(BlockViewport bv, List children) { + boolean inNewLayer = false; + if (maybeStartLayer(bv)) { + inNewLayer = true; + } // clip and position viewport if necessary if (bv.getPositioning() == Block.ABSOLUTE) { // save positions @@ -506,6 +515,7 @@ public abstract class AbstractRenderer currentIPPosition = saveIP; currentBPPosition = saveBP + bv.getAllocBPD(); } + maybeEndLayer(bv, inNewLayer); } /** @@ -573,6 +583,10 @@ public abstract class AbstractRenderer protected void renderBlock(Block block) { assert block != null; List children = block.getChildAreas(); + boolean inNewLayer = false; + if (maybeStartLayer(block)) { + inNewLayer = true; + } if (block instanceof BlockViewport) { if (children != null) { renderBlockViewport((BlockViewport) block, children); @@ -607,6 +621,45 @@ public abstract class AbstractRenderer currentBPPosition = saveBP + block.getAllocBPD(); } } + maybeEndLayer(block, inNewLayer); + } + + /** + * Establish new optional content group layer. + * @param layer name of layer + */ + protected abstract void startLayer(String layer); + + /** + * Finish current optional content group layer. + */ + protected abstract void endLayer(); + + protected boolean maybeStartLayer(Area area) { + String layer = (String) area.getTrait(Trait.LAYER); + if (layer != null) { + if (layers == null) { + layers = new Stack(); + } + if (layers.empty() || !layers.peek().equals(layer)) { + layers.push(layer); + startLayer(layer); + return true; + } + } + return false; + } + + protected void maybeEndLayer(Area area, boolean inNewLayer) { + if (inNewLayer) { + assert layers != null; + assert !layers.empty(); + String layer = (String) area.getTrait(Trait.LAYER); + assert layer != null; + assert layers.peek().equals(layer); + endLayer(); + layers.pop(); + } } /** @@ -746,6 +799,10 @@ public abstract class AbstractRenderer * @param ip the inline parent to render */ protected void renderInlineParent(InlineParent ip) { + boolean inNewLayer = false; + if (maybeStartLayer(ip)) { + inNewLayer = true; + } int level = ip.getBidiLevel(); List children = ip.getChildAreas(); renderInlineAreaBackAndBorders(ip); @@ -782,6 +839,7 @@ public abstract class AbstractRenderer } currentIPPosition = saveIP + ip.getAllocIPD(); currentBPPosition = saveBP; + maybeEndLayer(ip, inNewLayer); } /** diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 12713bb24..aaa702afa 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -164,7 +164,7 @@ public class AFPPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { try { saveGraphicsState(); concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 2dd046fa9..f69fe2091 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -126,8 +126,8 @@ public abstract class AbstractIFPainter implements } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(combine(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(combine(transforms), layer); } /** diff --git a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java index 868615360..c1742be1f 100644 --- a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java @@ -89,6 +89,7 @@ public class IFGraphicContext extends GraphicContext { public static class Group { private AffineTransform[] transforms; + private String layer; /** * Construct a Group. @@ -106,6 +107,16 @@ public class IFGraphicContext extends GraphicContext { this(new AffineTransform[] {transform}); } + /** + * Construct a layer Group, i.e., a Group with no transforms + * but with a optional content group layer label. + * @param layer a layer label + */ + public Group(String layer) { + this(); + this.layer = layer; + } + /** Default constructor. */ public Group() { this(EMPTY_TRANSFORM_ARRAY); @@ -116,12 +127,17 @@ public class IFGraphicContext extends GraphicContext { return this.transforms; } + /** @return layer */ + public String getLayer() { + return this.layer; + } + /** * @param painter a painter * @throws IFException in not caught */ public void start(IFPainter painter) throws IFException { - painter.startGroup(transforms); + painter.startGroup(transforms, layer); } /** @@ -136,6 +152,11 @@ public class IFGraphicContext extends GraphicContext { public String toString() { StringBuffer sb = new StringBuffer("group: "); IFUtil.toString(getTransforms(), sb); + if ((layer != null) && (layer.length() > 0)) { + sb.append(" layer("); + sb.append(layer); + sb.append(')'); + } return sb.toString(); } diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index 599292287..d43b5cb85 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -113,19 +113,21 @@ public interface IFPainter { /** * Starts a new group of graphical elements. Corresponds to SVG's g element. * @param transforms a series of transformation matrices establishing the new coordinate system + * @param layer an optional layer label (or null if none) * @throws IFException if an error occurs while handling this element */ - void startGroup(AffineTransform[] transforms) throws IFException; + void startGroup(AffineTransform[] transforms, String layer) throws IFException; /** * Starts a new group of graphical elements. Corresponds to SVG's g element. * @param transform the transformation matrix establishing the new coordinate system + * @param layer an optional layer label (or null if none) * @throws IFException if an error occurs while handling this element */ - void startGroup(AffineTransform transform) throws IFException; + void startGroup(AffineTransform transform, String layer) throws IFException; /** - * Ends the current group and restores the previous coordinate system. + * Ends the current group and restores the previous coordinate system (and layer). * @throws IFException if an error occurs while handling this element */ void endGroup() throws IFException; diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 1af1d4a06..519726291 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -592,7 +592,8 @@ public class IFParser implements IFConstants { String transform = attributes.getValue("transform"); AffineTransform[] transforms = AffineTransformArrayParser.createAffineTransform(transform); - painter.startGroup(transforms); + String layer = attributes.getValue("layer"); + painter.startGroup(transforms, layer); } public void endElement() throws IFException { @@ -800,8 +801,8 @@ public class IFParser implements IFConstants { } painter.drawImage(uri, new Rectangle(x, y, width, height)); } - resetForeignAttributes(); resetStructureTreeElement(); + resetForeignAttributes(); inForeignObject = false; } diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 30ceda108..e40a8f6b3 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -742,6 +742,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { protected void renderBlockViewport(BlockViewport bv, List children) { //Essentially the same code as in the super class but optimized for the IF + // Handle new layer. + boolean inNewLayer = false; + if (maybeStartLayer(bv)) { + inNewLayer = true; + } + //This is the content-rect Dimension dim = new Dimension(bv.getIPD(), bv.getBPD()); viewportDimensionStack.push(dim); @@ -842,6 +848,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { currentBPPosition += bv.getAllocBPD(); } viewportDimensionStack.pop(); + maybeEndLayer(bv, inNewLayer); } /** {@inheritDoc} */ @@ -849,7 +856,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { StructureTreeElement structElem = (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT); establishStructureTreeElement(structElem); - pushdID(viewport); + pushID(viewport); Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); viewportDimensionStack.push(dim); super.renderInlineViewport(viewport); @@ -895,10 +902,27 @@ public class IFRenderer extends AbstractPathOrientedRenderer { restoreGraphicsState(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + if (log.isTraceEnabled()) { + log.trace("startLayer() layer=" + layer); + } + saveGraphicsState(); + pushGroup(new IFGraphicContext.Group(layer)); + } + + /** {@inheritDoc} */ + protected void endLayer() { + if (log.isTraceEnabled()) { + log.trace("endLayer()"); + } + restoreGraphicsState(); + } + /** {@inheritDoc} */ protected void renderInlineArea(InlineArea inlineArea) { saveInlinePosIfTargetable(inlineArea); - pushdID(inlineArea); + pushID(inlineArea); super.renderInlineArea(inlineArea); popID(inlineArea); } @@ -965,7 +989,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { log.trace("renderBlock() " + block); } saveBlockPosIfTargetable(block); - pushdID(block); + pushID(block); IFContext context = documentHandler.getContext(); Locale oldLocale = context.getLanguage(); context.setLanguage(block.getLocale()); @@ -977,7 +1001,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { popID(block); } - private void pushdID(Area area) { + private void pushID(Area area) { String prodID = (String) area.getTrait(Trait.PROD_ID); if (prodID != null) { ids.push(prodID); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 1ffd42863..395e79719 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -428,21 +428,24 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler { } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(IFUtil.toString(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(IFUtil.toString(transforms), layer); } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - startGroup(IFUtil.toString(transform)); + public void startGroup(AffineTransform transform, String layer) throws IFException { + startGroup(IFUtil.toString(transform), layer); } - private void startGroup(String transform) throws IFException { + private void startGroup(String transform, String layer) throws IFException { try { AttributesImpl atts = new AttributesImpl(); if (transform != null && transform.length() > 0) { addAttribute(atts, "transform", transform); } + if (layer != null && layer.length() > 0) { + addAttribute(atts, "layer", layer); + } handler.startElement(EL_GROUP, atts); } catch (SAXException e) { throw new IFException("SAX error in startGroup()", e); diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 07440ff0b..328d1a4f8 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -143,7 +143,7 @@ public class Java2DPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java index a98aff353..d343c55d8 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java +++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java @@ -475,6 +475,14 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem restoreGraphicsState(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + } + + /** {@inheritDoc} */ + protected void endLayer() { + } + /** {@inheritDoc} */ protected List breakOutOfStateStack() { log.debug("Block.FIXED --> break out"); diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index c51ee834f..f934eed8c 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -126,7 +126,7 @@ public class PCLPainter extends AbstractIFPainter implements } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java index dde6b0ef3..ac7b1d905 100644 --- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -30,6 +30,7 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFPaintingState; +import org.apache.fop.pdf.PDFReference; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFStream; import org.apache.fop.pdf.PDFText; @@ -55,7 +56,7 @@ public class PDFContentGenerator { private PDFColorHandler colorHandler; /** drawing state */ - protected PDFPaintingState currentState = null; + protected PDFPaintingState currentState; /** Text generation utility holding the current font status */ protected PDFTextUtil textutil; @@ -156,15 +157,23 @@ public class PDFContentGenerator { */ protected void comment(String text) { if (WRITE_COMMENTS) { - currentStream.add("% " + text + "\n"); + getStream().add("% " + text + "\n"); } } /** Save graphics state. */ protected void saveGraphicsState() { endTextObject(); - currentState.save(); - currentStream.add("q\n"); + getState().save(); + getStream().add("q\n"); + } + + /** Save graphics state with optional layer. */ + protected void saveGraphicsState(String layer) { + endTextObject(); + getState().save(); + maybeBeginLayer(layer); + getStream().add("q\n"); } /** @@ -174,9 +183,9 @@ public class PDFContentGenerator { */ protected void saveGraphicsState(String structElemType, int sequenceNum) { endTextObject(); - currentState.save(); + getState().save(); beginMarkedContentSequence(structElemType, sequenceNum); - currentStream.add("q\n"); + getStream().add("q\n"); } /** @@ -208,18 +217,18 @@ public class PDFContentGenerator { if (structElemType != null) { String actualTextProperty = actualText == null ? "" : " /ActualText " + PDFText.escapeText(actualText); - currentStream.add(structElemType + " <>\n" + "BDC\n"); } else { - currentStream.add("/Artifact\nBMC\n"); + getStream().add("/Artifact\nBMC\n"); this.inArtifactMode = true; } this.inMarkedContentSequence = true; } void endMarkedContentSequence() { - currentStream.add("EMC\n"); + getStream().add("EMC\n"); this.inMarkedContentSequence = false; this.inArtifactMode = false; } @@ -231,9 +240,10 @@ public class PDFContentGenerator { */ protected void restoreGraphicsState(boolean popState) { endTextObject(); - currentStream.add("Q\n"); + getStream().add("Q\n"); + maybeEndLayer(); if (popState) { - currentState.restore(); + getState().restore(); } } @@ -251,11 +261,42 @@ public class PDFContentGenerator { */ protected void restoreGraphicsStateAccess() { endTextObject(); - currentStream.add("Q\n"); + getStream().add("Q\n"); if (this.inMarkedContentSequence) { endMarkedContentSequence(); } - currentState.restore(); + getState().restore(); + } + + private void maybeBeginLayer(String layer) { + if ((layer != null) && (layer.length() > 0)) { + getState().setLayer(layer); + beginOptionalContent(layer); + } + } + + private void maybeEndLayer() { + if (getState().getLayerChanged()) { + endOptionalContent(); + } + } + + private int ocNameIndex = 0; + + private void beginOptionalContent(String layerId) { + String name; + PDFReference layer = document.resolveExtensionReference(layerId); + if (layer != null) { + name = "oc" + ++ocNameIndex; + document.getResources().addProperty(name, layer); + } else { + name = "unknown"; + } + getStream().add("/OC /" + name + " BDC\n"); + } + + private void endOptionalContent() { + getStream().add("EMC\n"); } /** Indicates the beginning of a text object. */ @@ -310,8 +351,8 @@ public class PDFContentGenerator { public void concatenate(AffineTransform transform) { this.transform = transform; if (!transform.isIdentity()) { - currentState.concatenate(transform); - currentStream.add(CTMHelper.toPDFString(transform, false) + " cm\n"); + getState().concatenate(transform); + getStream().add(CTMHelper.toPDFString(transform, false) + " cm\n"); } } @@ -333,7 +374,7 @@ public class PDFContentGenerator { * @param content the PDF content */ public void add(String content) { - currentStream.add(content); + getStream().add(content); } /** @@ -350,9 +391,9 @@ public class PDFContentGenerator { * @param width line width in points */ public void updateLineWidth(float width) { - if (currentState.setLineWidth(width)) { + if (getState().setLineWidth(width)) { //Only write if value has changed WRT the current line width - currentStream.add(format(width) + " w\n"); + getStream().add(format(width) + " w\n"); } } @@ -362,7 +403,7 @@ public class PDFContentGenerator { */ public void updateCharacterSpacing(float value) { if (getState().setCharacterSpacing(value)) { - currentStream.add(format(value) + " Tc\n"); + getStream().add(format(value) + " Tc\n"); } } @@ -400,7 +441,7 @@ public class PDFContentGenerator { if (pdf != null) { colorHandler.establishColor(pdf, col, fill); } else { - setColor(col, fill, this.currentStream); + setColor(col, fill, getStream()); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 0a3d34ef4..648cdce7a 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -306,8 +306,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe); } } else if (extension instanceof PDFDictionaryAttachment) { - PDFDictionaryAttachment dictionaryExtension = (PDFDictionaryAttachment) extension; - pdfUtil.renderDictionaryExtension(dictionaryExtension, currentPage); + pdfUtil.renderDictionaryExtension((PDFDictionaryAttachment) extension, currentPage); } else if (extension != null) { log.debug("Don't know how to handle extension object. Ignoring: " + extension + " (" + extension.getClass().getName() + ")"); diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index d9fdd399c..f85328b8b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -143,8 +143,8 @@ public class PDFPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - generator.saveGraphicsState(); + public void startGroup(AffineTransform transform, String layer) throws IFException { + generator.saveGraphicsState(layer); generator.concatenate(toPoints(transform)); } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index 962cd7847..d27fead12 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -27,6 +27,7 @@ import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.EnumMap; +import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; @@ -55,24 +56,33 @@ import org.apache.fop.pdf.PDFFileSpec; import org.apache.fop.pdf.PDFICCBasedColorSpace; import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFInfo; +import org.apache.fop.pdf.PDFLayer; import org.apache.fop.pdf.PDFMetadata; import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFNames; +import org.apache.fop.pdf.PDFNavigator; +import org.apache.fop.pdf.PDFNull; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFOutputIntent; import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFPageLabels; import org.apache.fop.pdf.PDFReference; +import org.apache.fop.pdf.PDFSetOCGStateAction; import org.apache.fop.pdf.PDFText; +import org.apache.fop.pdf.PDFTransitionAction; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; import org.apache.fop.pdf.VersionController; +import org.apache.fop.render.pdf.extensions.PDFActionExtension; +import org.apache.fop.render.pdf.extensions.PDFArrayExtension; +import org.apache.fop.render.pdf.extensions.PDFCollectionEntryExtension; import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment; -import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryExtension; -import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryType; import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension; import org.apache.fop.render.pdf.extensions.PDFDictionaryType; import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment; +import org.apache.fop.render.pdf.extensions.PDFObjectType; +import org.apache.fop.render.pdf.extensions.PDFPageExtension; +import org.apache.fop.render.pdf.extensions.PDFReferenceExtension; import static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS; import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT; @@ -260,10 +270,189 @@ class PDFRenderingUtil { public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) { PDFDictionaryExtension extension = attachment.getExtension(); - if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { + PDFDictionaryType type = extension.getDictionaryType(); + if (type == PDFDictionaryType.Action) { + addNavigatorAction(extension); + } else if (type == PDFDictionaryType.Layer) { + addLayer(extension); + } else if (type == PDFDictionaryType.Navigator) { + addNavigator(extension); + } else { + renderDictionaryExtension(extension, currentPage); + } + } + + public void addLayer(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Layer; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + PDFLayer layer = pdfDoc.getFactory().makeLayer(id); + layer.setResolver(new PDFLayer.Resolver(layer, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object name = extension.findEntryValue("Name"); + Object intent = extension.findEntryValue("Intent"); + Object usage = makeDictionary(extension.findEntryValue("Usage")); + getLayer().populate(name, intent, usage); + } + }); + } + } + + public void addNavigatorAction(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Action; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + String type = extension.getProperty(PDFActionExtension.PROPERTY_TYPE); + if (type != null) { + if (type.equals("SetOCGState")) { + PDFSetOCGStateAction action = pdfDoc.getFactory().makeSetOCGStateAction(id); + action.setResolver(new PDFSetOCGStateAction.Resolver(action, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object state = makeArray(extension.findEntryValue("State")); + Object preserveRB = extension.findEntryValue("PreserveRB"); + Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next")); + getAction().populate(state, preserveRB, nextAction); + } + }); + } else if (type.equals("Trans")) { + PDFTransitionAction action = pdfDoc.getFactory().makeTransitionAction(id); + action.setResolver(new PDFTransitionAction.Resolver(action, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object transition = makeDictionary(extension.findEntryValue("Trans")); + Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next")); + getAction().populate(transition, nextAction); + } + }); + } else { + throw new UnsupportedOperationException(); + } + } + } + } + + public void addNavigator(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Navigator; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + PDFNavigator navigator = pdfDoc.getFactory().makeNavigator(id); + navigator.setResolver(new PDFNavigator.Resolver(navigator, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object nextAction = makeDictionary(extension.findEntryValue("NA")); + Object next = makeDictionary(extension.findEntryValue("Next")); + Object prevAction = makeDictionary(extension.findEntryValue("PA")); + Object prev = makeDictionary(extension.findEntryValue("Prev")); + Object duration = extension.findEntryValue("Dur"); + getNavigator().populate(nextAction, next, prevAction, prev, duration); + } + }); + } + } + + private Object makeArray(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + return populateArray(new PDFArray(), (List) value); + } else { + throw new IllegalArgumentException(); + } + } + + private Object populateArray(PDFArray array, List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + PDFObjectType type = entry.getType(); + if (type == PDFObjectType.Array) { + array.add(makeArray(entry.getValue())); + } else if (type == PDFObjectType.Boolean) { + array.add(entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Dictionary) { + array.add(makeDictionary(entry.getValue())); + } else if (type == PDFObjectType.Name) { + array.add(new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + array.add(new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + array.add(resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + array.add(entry.getValue()); + } + } + return array; + } + + private Object makeDictionary(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + return populateDictionary(new PDFDictionary(), (List) value); + } else { + throw new IllegalArgumentException(); + } + } + + private Object populateDictionary(PDFDictionary dictionary, List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + PDFObjectType type = entry.getType(); + String key = entry.getKey(); + if (type == PDFObjectType.Array) { + dictionary.put(key, makeArray(entry.getValue())); + } else if (type == PDFObjectType.Boolean) { + dictionary.put(key, entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Dictionary) { + dictionary.put(key, makeDictionary(entry.getValue())); + } else if (type == PDFObjectType.Name) { + dictionary.put(key, new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + dictionary.put(key, new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + dictionary.put(key, resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + dictionary.put(key, entry.getValue()); + } + } + return dictionary; + } + + private Object makeDictionaryOrArray(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + if (hasKeyedEntry((List) value)) { + return populateDictionary(new PDFDictionary(), (List) value); + } else { + return populateArray(new PDFArray(), (List) value); + } + } else { + throw new IllegalArgumentException(); + } + } + + private boolean hasKeyedEntry(List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + if (entry.getKey() != null) { + return true; + } + } + return false; + } + + public void renderDictionaryExtension(PDFDictionaryExtension extension, PDFPage currentPage) { + PDFDictionaryType type = extension.getDictionaryType(); + if (type == PDFDictionaryType.Catalog) { augmentDictionary(pdfDoc.getRoot(), extension); - } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { - if (extension.matchesPageNumber(currentPage.getPageIndex() + 1)) { + } else if (type == PDFDictionaryType.Page) { + assert extension instanceof PDFPageExtension; + if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) { augmentDictionary(currentPage, extension); } } else { @@ -272,9 +461,11 @@ class PDFRenderingUtil { } private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) { - for (PDFDictionaryEntryExtension entry : extension.getEntries()) { + for (PDFCollectionEntryExtension entry : extension.getEntries()) { if (entry instanceof PDFDictionaryExtension) { dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + } else if (entry instanceof PDFArrayExtension) { + dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry)); } else { augmentDictionary(dictionary, entry); } @@ -282,22 +473,68 @@ class PDFRenderingUtil { return dictionary; } - private void augmentDictionary(PDFDictionary dictionary, PDFDictionaryEntryExtension entry) { - PDFDictionaryEntryType type = entry.getType(); + private void augmentDictionary(PDFDictionary dictionary, PDFCollectionEntryExtension entry) { + PDFObjectType type = entry.getType(); String key = entry.getKey(); - if (type == PDFDictionaryEntryType.Boolean) { + if (type == PDFObjectType.Boolean) { dictionary.put(key, entry.getValueAsBoolean()); - } else if (type == PDFDictionaryEntryType.Name) { + } else if (type == PDFObjectType.Name) { dictionary.put(key, new PDFName(entry.getValueAsString())); - } else if (type == PDFDictionaryEntryType.Number) { + } else if (type == PDFObjectType.Number) { dictionary.put(key, new PDFNumber(entry.getValueAsNumber())); - } else if (type == PDFDictionaryEntryType.String) { + } else if (type == PDFObjectType.Reference) { + assert entry instanceof PDFReferenceExtension; + dictionary.put(key, resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { dictionary.put(key, entry.getValueAsString()); } else { throw new IllegalStateException(); } } + private Object resolveReference(PDFReferenceExtension entry) { + PDFReference reference = (PDFReference) entry.getResolvedReference(); + if (reference == null) { + reference = pdfDoc.resolveExtensionReference(entry.getReferenceId()); + if (reference != null) { + entry.setResolvedReference(reference); + } + return reference; + } + return PDFNull.INSTANCE; + } + + private PDFArray augmentArray(PDFArray array, PDFArrayExtension extension) { + for (PDFCollectionEntryExtension entry : extension.getEntries()) { + if (entry instanceof PDFDictionaryExtension) { + array.add(augmentDictionary(new PDFDictionary(array), (PDFDictionaryExtension) entry)); + } else if (entry instanceof PDFArrayExtension) { + array.add(augmentArray(new PDFArray(array), (PDFArrayExtension) entry)); + } else { + augmentArray(array, entry); + } + } + return array; + } + + private void augmentArray(PDFArray array, PDFCollectionEntryExtension entry) { + PDFObjectType type = entry.getType(); + if (type == PDFObjectType.Boolean) { + array.add(entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Name) { + array.add(new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + array.add(new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + assert entry instanceof PDFReferenceExtension; + array.add(resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + array.add(entry.getValueAsString()); + } else { + throw new IllegalStateException(); + } + } + public PDFDocument setupPDFDocument(OutputStream out) throws IOException { if (this.pdfDoc != null) { throw new IllegalStateException("PDFDocument already set up"); diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java deleted file mode 100644 index 9de7e95da..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -import org.apache.fop.fo.FONode; - -// CSOFF: LineLengthCheck - -/** - * Base class for the PDF dictionary related extension elements. - */ -public abstract class AbstractPDFDictionaryElement extends AbstractPDFExtensionElement { - - public static final String ATT_KEY = PDFDictionaryEntryExtension.PROPERTY_KEY; - - /** - * Default constructor - * - * @param parent parent of this node - * @see org.apache.fop.fo.FONode#FONode(FONode) - */ - public AbstractPDFDictionaryElement(FONode parent) { - super(parent); - } -} - diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java new file mode 100644 index 000000000..e4a5747e6 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:action. + */ +public class PDFActionElement extends PDFDictionaryElement { + + public static final String ATT_TYPE = PDFActionExtension.PROPERTY_TYPE; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFActionElement(FONode parent) { + super(parent, PDFDictionaryType.Action); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String type = attlist.getValue(ATT_TYPE); + if (type != null) { + getDictionaryExtension().setProperty(PDFActionExtension.PROPERTY_TYPE, type); + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java new file mode 100644 index 000000000..778b8a203 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFActionExtension extends PDFDictionaryExtension { + + public static final String PROPERTY_TYPE = "type"; + + PDFActionExtension() { + super(PDFDictionaryType.Action); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java new file mode 100644 index 000000000..1f3ba22b2 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:array. + */ +public class PDFArrayElement extends PDFCollectionEntryElement { + + private PDFArrayExtension extension; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFArrayElement(FONode parent) { + super(parent, PDFObjectType.Array, new PDFArrayExtension()); + } + + public PDFArrayExtension getArrayExtension() { + assert getExtension() instanceof PDFArrayExtension; + return (PDFArrayExtension) getExtension(); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + } + + @Override + protected void addChildNode(FONode child) throws FOPException { + PDFArrayExtension extension = getArrayExtension(); + if (child instanceof PDFCollectionEntryElement) { + PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension(); + if (entry.getKey() == null) { + extension.addEntry(entry); + } + } + } + + @Override + public void endOfNode() throws FOPException { + super.endOfNode(); + } + + @Override + public String getLocalName() { + return PDFObjectType.Array.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java new file mode 100644 index 000000000..80c6c94e4 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import java.util.List; +import java.util.Map; + +// CSOFF: LineLengthCheck + +public class PDFArrayExtension extends PDFCollectionExtension { + + private static final long serialVersionUID = -1L; + + private Map properties; + private List entries; + + PDFArrayExtension() { + super(PDFObjectType.Array); + this.properties = new java.util.HashMap(); + this.entries = new java.util.ArrayList(); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return getEntries(); + } + + public void setProperty(String name, String value) { + properties.put(name, value); + } + + public String getProperty(String name) { + return properties.get(name); + } + + @Override + public void addEntry(PDFCollectionEntryExtension entry) { + if (entry.getKey() != null) { + throw new IllegalArgumentException(); + } else { + entries.add(entry); + } + } + + public List getEntries() { + return entries; + } + + public PDFCollectionEntryExtension getLastEntry() { + if (entries.size() > 0) { + return entries.get(entries.size() - 1); + } else { + return null; + } + } + + @Override + public String getElementName() { + return PDFObjectType.Array.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java new file mode 100644 index 000000000..029357d22 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:catalog. + */ +public class PDFCatalogElement extends PDFDictionaryElement { + + PDFCatalogElement(FONode parent) { + super(parent, PDFDictionaryType.Catalog); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java new file mode 100644 index 000000000..381f6fbe8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFCatalogExtension extends PDFDictionaryExtension { + + PDFCatalogExtension() { + super(PDFDictionaryType.Catalog); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java new file mode 100644 index 000000000..4185ceef0 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for collection entries: pdf:{array,boolean,dictionary,name,number,reference,string}. The specific type + * of entry is established at construction type. + */ +public class PDFCollectionEntryElement extends AbstractPDFExtensionElement { + + public static final String ATT_KEY = PDFCollectionEntryExtension.PROPERTY_KEY; + + private PDFCollectionEntryExtension extension; + private StringBuffer characters; + + PDFCollectionEntryElement(FONode parent, PDFObjectType type, PDFCollectionEntryExtension extension) { + super(parent); + this.extension = extension; + } + + PDFCollectionEntryElement(FONode parent, PDFObjectType type) { + this(parent, type, createExtension(type)); + } + + private static PDFCollectionEntryExtension createExtension(PDFObjectType type) { + if (type == PDFObjectType.Reference) { + return new PDFReferenceExtension(); + } else { + return new PDFCollectionEntryExtension(type); + } + } + + public PDFCollectionEntryExtension getExtension() { + return extension; + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + if (parent instanceof PDFDictionaryElement) { + String key = attlist.getValue(ATT_KEY); + if (key == null) { + missingPropertyError(ATT_KEY); + } else if (key.length() == 0) { + invalidPropertyValueError(ATT_KEY, key, null); + } else { + extension.setKey(key); + } + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent instanceof PDFDictionaryElement) { + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); + } + } + } + + @Override + protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException { + if (capturePCData(extension.getType())) { + if (characters == null) { + characters = new StringBuffer((length < 16) ? 16 : length); + } + characters.append(data, start, length); + } + } + + private boolean capturePCData(PDFObjectType type) { + if (type == PDFObjectType.Array) { + return false; + } else if (type == PDFObjectType.Dictionary) { + return false; + } else { + return (type != PDFObjectType.Reference); + } + } + + @Override + public void endOfNode() throws FOPException { + if (capturePCData(extension.getType())) { + if (extension.getType() == PDFObjectType.Boolean) { + String value = (characters != null) ? characters.toString() : ""; + if (!value.equals("true") && !value.equals("false")) { + invalidPropertyValueError("", value, null); + } + extension.setValue(Boolean.valueOf(value)); + } else if (extension.getType() == PDFObjectType.Name) { + String value = (characters != null) ? characters.toString() : ""; + if (value.length() == 0) { + invalidPropertyValueError("", value, null); + } + extension.setValue(value); + } else if (extension.getType() == PDFObjectType.Number) { + String value = (characters != null) ? characters.toString() : ""; + try { + double d = Double.parseDouble(value); + if (Math.abs(Math.floor(d) - d) < 1E-10) { + extension.setValue(Long.valueOf((long) d)); + } else { + extension.setValue(Double.valueOf(d)); + } + } catch (NumberFormatException e) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() == PDFObjectType.String) { + String value = (characters != null) ? characters.toString() : ""; + extension.setValue(value); + } + } + super.endOfNode(); + } + + @Override + public String getLocalName() { + return extension.getType().elementName(); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java new file mode 100644 index 000000000..d28f1602f --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFCollectionEntryExtension extends PDFObjectExtension { + + public static final String PROPERTY_KEY = "key"; + + /* Non-empty key if used as dictionary entry, otherwise must be null. */ + private String key; + + PDFCollectionEntryExtension(PDFObjectType type) { + super(type); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java new file mode 100644 index 000000000..228d69c79 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public abstract class PDFCollectionExtension extends PDFCollectionEntryExtension { + + protected PDFCollectionExtension(PDFObjectType type) { + super(type); + } + + public abstract void addEntry(PDFCollectionEntryExtension entry); + + public abstract PDFCollectionEntryExtension getLastEntry(); + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java index 19e5ce07a..3832619ba 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java @@ -43,14 +43,13 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { } public void toSAX(ContentHandler handler) throws SAXException { - PDFDictionaryType dictionaryType = extension.getDictionaryType(); int pageNumber = 0; - if (dictionaryType == PDFDictionaryType.Page) { + if (extension instanceof PDFPageExtension) { if (handler instanceof GenerationHelperContentHandler) { Object context = ((GenerationHelperContentHandler) handler).getContentHandlerContext(); if (context instanceof IFContext) { int pageIndex = ((IFContext) context).getPageIndex(); - if ((pageIndex >= 0) && extension.matchesPageNumber(pageIndex + 1)) { + if ((pageIndex >= 0) && ((PDFPageExtension) extension).matchesPageNumber(pageIndex + 1)) { pageNumber = pageIndex + 1; } else { pageNumber = -1; @@ -69,24 +68,40 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { String qn = PREFIX + ":" + ln; attributes = extractIFAttributes(attributes, dictionary); handler.startElement(CATEGORY, ln, qn, attributes); - for (PDFDictionaryEntryExtension entry : dictionary.getEntries()) { + for (PDFCollectionEntryExtension entry : dictionary.getEntries()) { toSAX(handler, entry); } handler.endElement(CATEGORY, ln, qn); } - private void toSAX(ContentHandler handler, PDFDictionaryEntryExtension entry) throws SAXException { + private void toSAX(ContentHandler handler, PDFArrayExtension array) throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + String ln = array.getElementName(); + String qn = PREFIX + ":" + ln; + attributes = extractIFAttributes(attributes, array); + handler.startElement(CATEGORY, ln, qn, attributes); + for (PDFCollectionEntryExtension entry : array.getEntries()) { + toSAX(handler, entry); + } + handler.endElement(CATEGORY, ln, qn); + } + + private void toSAX(ContentHandler handler, PDFCollectionEntryExtension entry) throws SAXException { if (entry instanceof PDFDictionaryExtension) { toSAX(handler, (PDFDictionaryExtension) entry); + } else if (entry instanceof PDFArrayExtension) { + toSAX(handler, (PDFArrayExtension) entry); } else { AttributesImpl attributes = new AttributesImpl(); String ln = entry.getElementName(); String qn = PREFIX + ":" + ln; attributes = extractIFAttributes(attributes, entry); handler.startElement(CATEGORY, ln, qn, attributes); - char[] characters = entry.getValueAsXMLEscapedString().toCharArray(); - if (characters.length > 0) { - handler.characters(characters, 0, characters.length); + if (!(entry instanceof PDFReferenceExtension)) { + char[] characters = entry.getValueAsXMLEscapedString().toCharArray(); + if (characters.length > 0) { + handler.characters(characters, 0, characters.length); + } } handler.endElement(CATEGORY, ln, qn); } @@ -94,16 +109,27 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) { PDFDictionaryType type = dictionary.getDictionaryType(); - if (type == PDFDictionaryType.Catalog) { - // no specific attriburtes + if (dictionary.usesIDAttribute()) { + String idName = PDFDictionaryElement.ATT_ID; + String id = dictionary.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if (id != null) { + attributes.addAttribute(null, idName, idName, "ID", id); + } + } + if (type == PDFDictionaryType.Action) { + String actionTypeName = PDFActionElement.ATT_TYPE; + String actionType = dictionary.getProperty(PDFActionExtension.PROPERTY_TYPE); + if (actionType != null) { + attributes.addAttribute(null, actionTypeName, actionTypeName, "CDATA", actionType); + } } else if (type == PDFDictionaryType.Page) { - String pageNumbersName = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + String pageNumbersName = PDFPageExtension.PROPERTY_PAGE_NUMBERS; String pageNumbers = dictionary.getProperty(pageNumbersName); if (pageNumbers != null) { attributes.addAttribute(null, pageNumbersName, pageNumbersName, "CDATA", pageNumbers); } } else if (type == PDFDictionaryType.Dictionary) { - String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + String keyName = PDFCollectionEntryElement.ATT_KEY; String key = dictionary.getKey(); if (key != null) { attributes.addAttribute(null, keyName, keyName, "CDATA", key); @@ -112,12 +138,28 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { return attributes; } - private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryEntryExtension entry) { - String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFArrayExtension array) { + String keyName = PDFCollectionEntryExtension.PROPERTY_KEY; + String key = array.getKey(); + if (key != null) { + attributes.addAttribute(null, keyName, keyName, "CDATA", key); + } + return attributes; + } + + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFCollectionEntryExtension entry) { + String keyName = PDFCollectionEntryElement.ATT_KEY; String key = entry.getKey(); if (key != null) { attributes.addAttribute(null, keyName, keyName, "CDATA", key); } + if (entry instanceof PDFReferenceExtension) { + String refid = ((PDFReferenceExtension) entry).getReferenceId(); + if (refid != null) { + String refidName = PDFReferenceElement.ATT_REFID; + attributes.addAttribute(null, refidName, refidName, "IDREF", refid); + } + } return attributes; } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java index 0920f3a78..9dc127da6 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java @@ -23,7 +23,6 @@ import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.extensions.ExtensionAttachment; @@ -34,38 +33,54 @@ import org.apache.fop.fo.extensions.ExtensionAttachment; * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type * of dictionary is established at construction type. */ -public class PDFDictionaryElement extends AbstractPDFDictionaryElement { +public class PDFDictionaryElement extends PDFCollectionEntryElement { - public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; - - private PDFDictionaryExtension extension; + public static final String ATT_ID = PDFDictionaryExtension.PROPERTY_ID; /** * Main constructor * @param parent parent FO node */ PDFDictionaryElement(FONode parent, PDFDictionaryType type) { - super(parent); - this.extension = new PDFDictionaryExtension(type); + super(parent, PDFObjectType.Dictionary, createExtension(type)); + } + + private static PDFDictionaryExtension createExtension(PDFDictionaryType type) { + if (type == PDFDictionaryType.Action) { + return new PDFActionExtension(); + } else if (type == PDFDictionaryType.Catalog) { + return new PDFCatalogExtension(); + } else if (type == PDFDictionaryType.Layer) { + return new PDFLayerExtension(); + } else if (type == PDFDictionaryType.Navigator) { + return new PDFNavigatorExtension(); + } else if (type == PDFDictionaryType.Page) { + return new PDFPageExtension(); + } else { + return new PDFDictionaryExtension(type); + } } - public PDFDictionaryExtension getExtension() { - return extension; + public PDFDictionaryExtension getDictionaryExtension() { + assert getExtension() instanceof PDFDictionaryExtension; + return (PDFDictionaryExtension) getExtension(); } @Override public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { - if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { - // no specific properties - } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { - String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS); - if (pageNumbers != null) { - extension.setProperty(ATT_PAGE_NUMBERS, pageNumbers); + PDFDictionaryExtension extension = getDictionaryExtension(); + if (extension.usesIDAttribute()) { + String id = attlist.getValue(ATT_ID); + if (id != null) { + extension.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); } - } else if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { + } + if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { String key = attlist.getValue(ATT_KEY); if (key == null) { - missingPropertyError(ATT_KEY); + if (parent instanceof PDFDictionaryElement) { + missingPropertyError(ATT_KEY); + } } else if (key.length() == 0) { invalidPropertyValueError(ATT_KEY, key, null); } else { @@ -78,16 +93,18 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { public void startOfNode() throws FOPException { super.startOfNode(); String localName = getLocalName(); - if (localName.equals("catalog")) { - if (parent.getNameId() != Constants.FO_DECLARATIONS) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); - } + if (localName.equals("action")) { + // handled in PDFActionElement subclass + } else if (localName.equals("catalog")) { + // handled in PDFCatalogElement subclass + } else if (localName.equals("layer")) { + // handled in PDFLayerElement subclass + } else if (localName.equals("navigator")) { + // handled in PDFNavigattorElement subclass } else if (localName.equals("page")) { - if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM"); - } + // handled in PDFPageElement subclass } else if (localName.equals("dictionary")) { - if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) { invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); } } else { @@ -97,14 +114,15 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { @Override protected void addChildNode(FONode child) throws FOPException { + PDFDictionaryExtension extension = getDictionaryExtension(); if (child instanceof PDFDictionaryElement) { - PDFDictionaryExtension extension = ((PDFDictionaryElement) child).getExtension(); - if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { - this.extension.addEntry(extension); + PDFDictionaryExtension entry = ((PDFDictionaryElement) child).getDictionaryExtension(); + if (entry.getDictionaryType() == PDFDictionaryType.Dictionary) { + extension.addEntry(entry); } - } else if (child instanceof PDFDictionaryEntryElement) { - PDFDictionaryEntryExtension extension = ((PDFDictionaryEntryElement) child).getExtension(); - this.extension.addEntry(extension); + } else if (child instanceof PDFCollectionEntryElement) { + PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension(); + extension.addEntry(entry); } } @@ -115,12 +133,13 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { @Override public String getLocalName() { + PDFDictionaryExtension extension = getDictionaryExtension(); return extension.getDictionaryType().elementName(); } @Override protected ExtensionAttachment instantiateExtensionAttachment() { - return new PDFDictionaryAttachment(extension); + return new PDFDictionaryAttachment(getDictionaryExtension()); } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java deleted file mode 100644 index bcdb90c2c..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FONode; -import org.apache.fop.fo.PropertyList; - -// CSOFF: LineLengthCheck - -/** - * Extension element for dictionary entries: pdf:{boolean,name,number,string}. The specific type - * of entry is established at construction type. - */ -public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement { - - private PDFDictionaryEntryExtension extension; - private StringBuffer characters; - - /** - * Main constructor - * @param parent parent FO node - */ - PDFDictionaryEntryElement(FONode parent, PDFDictionaryEntryType type) { - super(parent); - this.extension = new PDFDictionaryEntryExtension(type); - } - - public PDFDictionaryEntryExtension getExtension() { - return extension; - } - - @Override - public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { - String key = attlist.getValue("key"); - if (key == null) { - missingPropertyError("key"); - } else if (key.length() == 0) { - invalidPropertyValueError("key", key, null); - } else { - extension.setKey(key); - } - } - - @Override - public void startOfNode() throws FOPException { - super.startOfNode(); - if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); - } - } - - @Override - protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException { - if (characters == null) { - characters = new StringBuffer((length < 16) ? 16 : length); - } - characters.append(data, start, length); - } - - @Override - public void endOfNode() throws FOPException { - String value = (characters != null) ? characters.toString() : ""; - if (extension.getType() == PDFDictionaryEntryType.Boolean) { - if (!value.equals("true") && !value.equals("false")) { - invalidPropertyValueError("", value, null); - } - } else if (extension.getType() == PDFDictionaryEntryType.Name) { - if (value.length() == 0) { - invalidPropertyValueError("", value, null); - } - } else if (extension.getType() == PDFDictionaryEntryType.Number) { - try { - Double.valueOf(value); - } catch (NumberFormatException e) { - invalidPropertyValueError("", value, null); - } - } else if (extension.getType() != PDFDictionaryEntryType.String) { - throw new IllegalStateException(); - } - extension.setValue(value); - super.endOfNode(); - } - - @Override - public String getLocalName() { - return extension.getType().elementName(); - } -} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java deleted file mode 100644 index 94d6b5dbf..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -import org.apache.fop.util.XMLUtil; - -// CSOFF: LineLengthCheck - -public class PDFDictionaryEntryExtension { - - public static final String PROPERTY_KEY = "key"; - - private PDFDictionaryEntryType type; - private String key = ""; - private Object value; - - PDFDictionaryEntryExtension() { - } - - PDFDictionaryEntryExtension(PDFDictionaryEntryType type) { - this.type = type; - } - - public PDFDictionaryEntryType getType() { - return type; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public void setValue(String value) { - this.value = value; - } - - public Object getValue() { - return value; - } - - /** - * Obtain entry value as Boolean. - * @return entry value - */ - public Boolean getValueAsBoolean() { - if (value instanceof String) { - return Boolean.valueOf((String)value); - } else { - return false; - } - } - - /** - * Obtain entry value as Number. - * @return entry value - */ - public Number getValueAsNumber() { - if (value instanceof String) { - double d = Double.parseDouble((String) value); - if (Math.floor(d) == d) { - return Long.valueOf((long) d); - } else { - return Double.valueOf(d); - } - } else { - return Integer.valueOf(0); - } - } - - public String getValueAsString() { - if (value instanceof String) { - return (String) value; - } else { - return ""; - } - } - - public String getValueAsXMLEscapedString() { - return XMLUtil.escape(getValueAsString()); - } - - public void setValue(Object value) { - this.value = value; - } - - public String getElementName() { - return type.elementName(); - } - -} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java deleted file mode 100644 index e4b25819a..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -// CSOFF: LineLengthCheck - -/** - * Enumeration type for leaf PDF dictionary entry extension elements. - */ -public enum PDFDictionaryEntryType { - Boolean("boolean"), // boolean valued entry - Dictionary("dictionary"), // dictionary valued entry - Name("name"), // name valued entry - Number("number"), // number valued entry - String("string"); // string valued entry - - private String elementName; - PDFDictionaryEntryType(String elementName) { - this.elementName = elementName; - } - public String elementName() { - return elementName; - } - static PDFDictionaryEntryType valueOfElementName(String elementName) { - for (PDFDictionaryEntryType type : values()) { - if (type.elementName.equals(elementName)) { - return type; - } - } - throw new IllegalArgumentException(); - } - static boolean hasValueOfElementName(String elementName) { - try { - return valueOfElementName(elementName) != null; - } catch (Exception e) { - return false; - } - } -} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java index d4b11cdb4..50b6f3a83 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java @@ -24,24 +24,36 @@ import java.util.Map; // CSOFF: LineLengthCheck -public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { +public class PDFDictionaryExtension extends PDFCollectionExtension { + public static final String PROPERTY_ID = "id"; public static final String PROPERTY_PAGE_NUMBERS = "page-numbers"; private static final long serialVersionUID = -1L; private PDFDictionaryType dictionaryType; private Map properties; - private List entries; + private List entries; PDFDictionaryExtension() { + this(PDFDictionaryType.Dictionary); } PDFDictionaryExtension(PDFDictionaryType dictionaryType) { - super(PDFDictionaryEntryType.Dictionary); + super(PDFObjectType.Dictionary); this.dictionaryType = dictionaryType; this.properties = new java.util.HashMap(); - this.entries = new java.util.ArrayList(); + this.entries = new java.util.ArrayList(); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return getEntries(); } public PDFDictionaryType getDictionaryType() { @@ -56,15 +68,40 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { return properties.get(name); } - public void addEntry(PDFDictionaryEntryExtension entry) { - entries.add(entry); + @Override + public void addEntry(PDFCollectionEntryExtension entry) { + if ((entry.getKey() == null) || (entry.getKey().length() == 0)) { + throw new IllegalArgumentException(); + } else { + entries.add(entry); + } } - public List getEntries() { + public List getEntries() { return entries; } - public PDFDictionaryEntryExtension getLastEntry() { + public PDFCollectionEntryExtension findEntry(String key) { + for (PDFCollectionEntryExtension entry : entries) { + String entryKey = entry.getKey(); + if ((entryKey != null) && entryKey.equals(key)) { + return entry; + } + } + return null; + } + + public Object findEntryValue(String key) { + for (PDFCollectionEntryExtension entry : entries) { + String entryKey = entry.getKey(); + if ((entryKey != null) && entryKey.equals(key)) { + return entry.getValue(); + } + } + return null; + } + + public PDFCollectionEntryExtension getLastEntry() { if (entries.size() > 0) { return entries.get(entries.size() - 1); } else { @@ -72,48 +109,8 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { } } - /** - * Determine if page dictionary and page number matches. - * @param pageNumber page number, where first page number is 1 - * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property - */ - public boolean matchesPageNumber(int pageNumber) { - if (dictionaryType != PDFDictionaryType.Page) { - return false; - } - String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); - if ((pageNumbers == null) || (pageNumbers.length() == 0)) { - return false; - } else if (pageNumbers.equals("*")) { - return true; - } else { - for (String interval : pageNumbers.split("\\s*,\\s*")) { - String[] components = interval.split("\\s*-\\s*"); - if (components.length < 1) { - continue; - } else { - try { - int start = Integer.parseInt(components[0]); - int end = 0; - if (components.length > 1) { - if (!components[1].equals("LAST")) { - end = Integer.parseInt(components[1]); - } - } - if ((end == 0) && (pageNumber == start)) { - return true; - } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) { - return true; - } else { - continue; - } - } catch (NumberFormatException e) { - continue; - } - } - } - } - return false; + public boolean usesIDAttribute() { + return dictionaryType.usesIDAttribute(); } @Override diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java index edd95160a..a49a5fc8c 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java @@ -25,17 +25,28 @@ package org.apache.fop.render.pdf.extensions; * Enumeration type for PDF dictionary extension elements. */ public enum PDFDictionaryType { - Dictionary("dictionary"), // generic (nested) dictionary element - Catalog("catalog"), // catalog dictionary element - Page("page"); // page dictionary element + Action("action", true), // action dictionary element + Catalog("catalog"), // catalog dictionary element + Dictionary("dictionary"), // generic (nested) dictionary element + Layer("layer", true), // optional content group dictionary element + Navigator("navigator", true), // navigation node dictionary element + Page("page"); // page dictionary element private String elementName; - PDFDictionaryType(String elementName) { + private boolean usesIDAttribute; + PDFDictionaryType(String elementName, boolean usesIDAttribute) { this.elementName = elementName; + this.usesIDAttribute = usesIDAttribute; + } + PDFDictionaryType(String elementName) { + this(elementName, false); } public String elementName() { return elementName; } + public boolean usesIDAttribute() { + return usesIDAttribute; + } static PDFDictionaryType valueOfElementName(String elementName) { for (PDFDictionaryType type : values()) { if (type.elementName.equals(elementName)) { diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java index 3e063e24b..1fba80796 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java @@ -41,17 +41,56 @@ public class PDFElementMapping extends ElementMapping { protected void initialize() { if (foObjs == null) { foObjs = new java.util.HashMap(); + // pdf:action + foObjs.put(PDFDictionaryType.Action.elementName(), new PDFActionElementMaker()); + // pdf:array + foObjs.put(PDFObjectType.Array.elementName(), new PDFArrayElementMaker()); + // pdf:boolean + foObjs.put(PDFObjectType.Boolean.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Boolean)); + // pdf:catalog + foObjs.put(PDFDictionaryType.Catalog.elementName(), new PDFCatalogElementMaker()); + // pdf:dictionary + foObjs.put(PDFDictionaryType.Dictionary.elementName(), new PDFDictionaryElementMaker()); // pdf:embedded-file foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileElementMaker()); - // pdf:{catalog,page} et al. - for (PDFDictionaryType type : PDFDictionaryType.values()) { - foObjs.put(type.elementName(), new PDFDictionaryElementMaker(type)); - } - for (PDFDictionaryEntryType type : PDFDictionaryEntryType.values()) { - if (type != PDFDictionaryEntryType.Dictionary) { - foObjs.put(type.elementName(), new PDFDictionaryEntryElementMaker(type)); - } - } + // pdf:name + foObjs.put(PDFObjectType.Name.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Name)); + // pdf:number + foObjs.put(PDFObjectType.Number.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Number)); + // pdf:navigator + foObjs.put(PDFDictionaryType.Navigator.elementName(), new PDFNavigatorElementMaker()); + // pdf:layer + foObjs.put(PDFDictionaryType.Layer.elementName(), new PDFLayerElementMaker()); + // pdf:page + foObjs.put(PDFDictionaryType.Page.elementName(), new PDFPageElementMaker()); + // pdf:reference + foObjs.put(PDFObjectType.Reference.elementName(), new PDFReferenceElementMaker()); + // pdf:string + foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String)); + } + } + + static class PDFActionElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFActionElement(parent); + } + } + + static class PDFArrayElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFArrayElement(parent); + } + } + + static class PDFCatalogElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFCatalogElement(parent); + } + } + + static class PDFDictionaryElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFDictionaryElement(parent, PDFDictionaryType.Dictionary); } } @@ -61,23 +100,38 @@ public class PDFElementMapping extends ElementMapping { } } - static class PDFDictionaryElementMaker extends ElementMapping.Maker { - private PDFDictionaryType dictionaryType; - PDFDictionaryElementMaker(PDFDictionaryType dictionaryType) { - this.dictionaryType = dictionaryType; + static class PDFLayerElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFLayerElement(parent); } + } + + static class PDFNavigatorElementMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new PDFDictionaryElement(parent, dictionaryType); + return new PDFNavigatorElement(parent); } } - static class PDFDictionaryEntryElementMaker extends ElementMapping.Maker { - private PDFDictionaryEntryType entryType; - PDFDictionaryEntryElementMaker(PDFDictionaryEntryType entryType) { + static class PDFPageElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFPageElement(parent); + } + } + + static class PDFCollectionEntryElementMaker extends ElementMapping.Maker { + private PDFObjectType entryType; + PDFCollectionEntryElementMaker(PDFObjectType entryType) { this.entryType = entryType; } public FONode make(FONode parent) { - return new PDFDictionaryEntryElement(parent, entryType); + return new PDFCollectionEntryElement(parent, entryType); } } + + static class PDFReferenceElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFReferenceElement(parent); + } + } + } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java index f14f1e7d6..2fd14058e 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java @@ -49,7 +49,7 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle private Attributes lastAttributes; // PDFDictionaryAttachment related - private Stack dictionaries = new Stack(); + private Stack collections = new Stack(); private boolean captureContent; private StringBuffer characters; @@ -58,29 +58,78 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle if (PDFExtensionAttachment.CATEGORY.equals(uri)) { if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) { lastAttributes = new AttributesImpl(attributes); - } else if (PDFDictionaryType.hasValueOfElementName(localName)) { - PDFDictionaryExtension dictionary = new PDFDictionaryExtension(PDFDictionaryType.valueOfElementName(localName)); - String key = attributes.getValue(PDFDictionaryEntryExtension.PROPERTY_KEY); + } else if (PDFDictionaryType.Action.elementName().equals(localName)) { + PDFActionExtension action = new PDFActionExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + action.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + String type = attributes.getValue(PDFActionElement.ATT_TYPE); + if (type != null) { + action.setProperty(PDFActionExtension.PROPERTY_TYPE, type); + } + collections.push(action); + } else if (PDFObjectType.Array.elementName().equals(localName)) { + PDFArrayExtension array = new PDFArrayExtension(); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); + if (key != null) { + array.setKey(key); + } + collections.push(array); + } else if (PDFDictionaryType.Catalog.elementName().equals(localName)) { + PDFCatalogExtension catalog = new PDFCatalogExtension(); + collections.push(catalog); + } else if (PDFDictionaryType.Dictionary.elementName().equals(localName)) { + PDFDictionaryExtension dictionary = new PDFDictionaryExtension(); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); if (key != null) { dictionary.setKey(key); } - if (dictionary.getDictionaryType() == PDFDictionaryType.Page) { - String pageNumbers = attributes.getValue(PDFDictionaryElement.ATT_PAGE_NUMBERS); - if (pageNumbers != null) { - dictionary.setProperty(PDFDictionaryElement.ATT_PAGE_NUMBERS, pageNumbers); - } + collections.push(dictionary); + } else if (PDFDictionaryType.Layer.elementName().equals(localName)) { + PDFLayerExtension layer = new PDFLayerExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + layer.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + collections.push(layer); + } else if (PDFDictionaryType.Navigator.elementName().equals(localName)) { + PDFNavigatorExtension navigator = new PDFNavigatorExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + navigator.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + collections.push(navigator); + } else if (PDFDictionaryType.Page.elementName().equals(localName)) { + PDFPageExtension page = new PDFPageExtension(); + String pageNumbers = attributes.getValue(PDFPageElement.ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers); + } + collections.push(page); + } else if (PDFObjectType.hasValueOfElementName(localName)) { + PDFCollectionEntryExtension entry; + if (PDFObjectType.Reference.elementName().equals(localName)) { + entry = new PDFReferenceExtension(); + } else { + entry = new PDFCollectionEntryExtension(PDFObjectType.valueOfElementName(localName)); } - dictionaries.push(dictionary); - } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { - PDFDictionaryEntryExtension entry = new PDFDictionaryEntryExtension(PDFDictionaryEntryType.valueOfElementName(localName)); - String key = attributes.getValue(PDFDictionaryEntryElement.ATT_KEY); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); if (key != null) { entry.setKey(key); } - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.peek(); - dictionary.addEntry(entry); - captureContent = true; + if (entry instanceof PDFReferenceExtension) { + String refid = attributes.getValue(PDFReferenceElement.ATT_REFID); + if (refid != null) { + ((PDFReferenceExtension) entry).setReferenceId(refid); + } + } + if (!collections.empty()) { + PDFCollectionExtension collection = collections.peek(); + collection.addEntry(entry); + if (!(entry instanceof PDFReferenceExtension)) { + captureContent = true; + } } } else { throw new SAXException("Unhandled element " + localName + " in namespace: " + uri); @@ -107,33 +156,48 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle String name = lastAttributes.getValue("name"); String src = lastAttributes.getValue("src"); String desc = lastAttributes.getValue("description"); + this.lastAttributes = null; this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc); } else if (PDFDictionaryType.hasValueOfElementName(localName)) { - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.pop(); - if ((dictionary.getDictionaryType() == PDFDictionaryType.Catalog) || (dictionary.getDictionaryType() == PDFDictionaryType.Page)) { + if (!collections.empty() && (collections.peek() instanceof PDFDictionaryExtension)) { + PDFDictionaryExtension dictionary = (PDFDictionaryExtension) collections.pop(); + if (!collections.empty()) { + PDFCollectionExtension collectionOuter = collections.peek(); + collectionOuter.addEntry(dictionary); + } else if (dictionary.getDictionaryType() != PDFDictionaryType.Dictionary) { this.returnedObject = new PDFDictionaryAttachment(dictionary); - } else if (!dictionaries.empty()) { - PDFDictionaryExtension dictionaryOuter = dictionaries.peek(); - dictionaryOuter.addEntry(dictionary); + } else { + throw new SAXException(new IllegalStateException("generic dictionary not permitted at outer level")); + } + } else { + throw new SAXException(new IllegalStateException("collections stack is empty or not a dictionary")); + } + } else if (PDFObjectType.Array.elementName().equals(localName)) { + if (!collections.empty() && (collections.peek() instanceof PDFArrayExtension)) { + PDFArrayExtension array = (PDFArrayExtension) collections.pop(); + if (!collections.empty()) { + PDFCollectionExtension collectionOuter = collections.peek(); + collectionOuter.addEntry(array); + } else { + throw new SAXException(new IllegalStateException("array not permitted at outer level")); } } else { - throw new SAXException(new IllegalStateException("no active dictionary")); + throw new SAXException(new IllegalStateException("collections stack is empty or not an array")); } - } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.peek(); - PDFDictionaryEntryExtension entry = dictionary.getLastEntry(); + } else if (PDFObjectType.hasValueOfElementName(localName)) { + if (!collections.empty()) { + PDFCollectionExtension collection = collections.peek(); + PDFCollectionEntryExtension entry = collection.getLastEntry(); if (entry != null) { if (characters != null) { entry.setValue(characters.toString()); characters = null; } } else { - throw new SAXException(new IllegalStateException("no active entry")); + throw new SAXException(new IllegalStateException("no current entry")); } } else { - throw new SAXException(new IllegalStateException("no active dictionary")); + throw new SAXException(new IllegalStateException("entry not permitted at outer level")); } } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java new file mode 100644 index 000000000..dab0ecf78 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:layer. + */ +public class PDFLayerElement extends PDFDictionaryElement { + + PDFLayerElement(FONode parent) { + super(parent, PDFDictionaryType.Layer); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java new file mode 100644 index 000000000..d6cc27ab8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFLayerExtension extends PDFDictionaryExtension { + + PDFLayerExtension() { + super(PDFDictionaryType.Layer); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java new file mode 100644 index 000000000..5c5b779b5 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:navigator. + */ +public class PDFNavigatorElement extends PDFDictionaryElement { + + PDFNavigatorElement(FONode parent) { + super(parent, PDFDictionaryType.Navigator); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java new file mode 100644 index 000000000..ee9dfa7c8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFNavigatorExtension extends PDFDictionaryExtension { + + PDFNavigatorExtension() { + super(PDFDictionaryType.Navigator); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java new file mode 100644 index 000000000..5447d87f2 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.util.XMLUtil; + +// CSOFF: LineLengthCheck + +public class PDFObjectExtension { + + private PDFObjectType type; + private Object value; + + PDFObjectExtension(PDFObjectType type) { + this.type = type; + } + + public PDFObjectType getType() { + return type; + } + + public void setValue(Object value) { + this.value = value; + } + + public Object getValue() { + return value; + } + + /** + * Obtain entry value as Boolean. + * @return entry value + */ + public Boolean getValueAsBoolean() { + Object value = getValue(); + if (value instanceof Boolean) { + return (Boolean) value; + } else if (value instanceof String) { + return Boolean.valueOf((String)value); + } else { + return false; + } + } + + /** + * Obtain entry value as Number. + * @return entry value + */ + public Number getValueAsNumber() { + Object value = getValue(); + if (value instanceof Number) { + return (Number) value; + } else if (value instanceof String) { + double d = Double.parseDouble((String) value); + if (Math.abs(Math.floor(d) - d) < 1E-10) { + return Long.valueOf((long) d); + } else { + return Double.valueOf(d); + } + } else { + return Integer.valueOf(0); + } + } + + public String getValueAsString() { + Object value = getValue(); + if (value == null) { + return null; + } else if (value instanceof String) { + return (String) value; + } else { + return value.toString(); + } + } + + public String getValueAsXMLEscapedString() { + return XMLUtil.escape(getValueAsString()); + } + + public String getElementName() { + return type.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java new file mode 100644 index 000000000..c193a3b7b --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +/** + * Enumeration type for leaf PDF object extension types used as singletons, + * dictionary entries, or array entries. + */ +public enum PDFObjectType { + Array("array"), // array valued entry + Boolean("boolean"), // boolean valued entry + Dictionary("dictionary"), // dictionary valued entry + Name("name"), // name valued entry + Number("number"), // number valued entry + Reference("reference"), // indirect object reference entry + String("string"); // string valued entry + + private String elementName; + PDFObjectType(String elementName) { + this.elementName = elementName; + } + public String elementName() { + return elementName; + } + static PDFObjectType valueOfElementName(String elementName) { + for (PDFObjectType type : values()) { + if (type.elementName.equals(elementName)) { + return type; + } + } + throw new IllegalArgumentException(); + } + static boolean hasValueOfElementName(String elementName) { + try { + valueOfElementName(elementName); + return true; + } catch (Exception e) { + return false; + } + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java new file mode 100644 index 000000000..28a966ead --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:page. + */ +public class PDFPageElement extends PDFDictionaryElement { + + public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFPageElement(FONode parent) { + super(parent, PDFDictionaryType.Page); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + getDictionaryExtension().setProperty(PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS, pageNumbers); + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java new file mode 100644 index 000000000..0726a4e55 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFPageExtension extends PDFDictionaryExtension { + + public static final String PROPERTY_PAGE_NUMBERS = "page-numbers"; + + PDFPageExtension() { + super(PDFDictionaryType.Page); + } + + /** + * Determine if page dictionary and page number matches. + * @param pageNumber page number, where first page number is 1 + * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property + */ + public boolean matchesPageNumber(int pageNumber) { + String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); + if ((pageNumbers == null) || (pageNumbers.length() == 0)) { + return false; + } else if (pageNumbers.equals("*")) { + return true; + } else { + for (String interval : pageNumbers.split("\\s*,\\s*")) { + String[] components = interval.split("\\s*-\\s*"); + if (components.length < 1) { + continue; + } else { + try { + int start = Integer.parseInt(components[0]); + int end = 0; + if (components.length > 1) { + if (!components[1].equals("LAST")) { + end = Integer.parseInt(components[1]); + } + } + if ((end == 0) && (pageNumber == start)) { + return true; + } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) { + return true; + } else { + continue; + } + } catch (NumberFormatException e) { + continue; + } + } + } + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java new file mode 100644 index 000000000..37aeeb890 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:reference. + */ +public class PDFReferenceElement extends PDFCollectionEntryElement { + + public static final String ATT_REFID = PDFReferenceExtension.PROPERTY_REFID; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFReferenceElement(FONode parent) { + super(parent, PDFObjectType.Reference); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String refid = attlist.getValue(ATT_REFID); + if (refid == null) { + missingPropertyError(ATT_REFID); + } else if (refid.length() == 0) { + invalidPropertyValueError(ATT_REFID, refid, null); + } else { + ((PDFReferenceExtension) getExtension()).setReferenceId(refid); + } + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java new file mode 100644 index 000000000..09621c01c --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFReferenceExtension extends PDFCollectionEntryExtension { + + public static final String PROPERTY_REFID = "refid"; + + private String refid; + private Object resolvedReference; + + PDFReferenceExtension() { + super(PDFObjectType.Reference); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return this; + } + + public String getReferenceId() { + return refid; + } + + public void setReferenceId(String refid) { + this.refid = refid; + } + + public Object getResolvedReference() { + return resolvedReference; + } + + public void setResolvedReference(Object resolvedReference) { + this.resolvedReference = resolvedReference; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index 3d1887f2d..547662c47 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -116,7 +116,7 @@ public class PSPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { try { PSGenerator generator = getGenerator(); saveGraphicsState(); diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java index bf4a46e19..5b398711f 100644 --- a/src/java/org/apache/fop/render/txt/TXTRenderer.java +++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java @@ -582,6 +582,14 @@ public class TXTRenderer extends AbstractPathOrientedRenderer { currentState.pop(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + } + + /** {@inheritDoc} */ + protected void endLayer() { + } + /** {@inheritDoc} */ protected void concatenateTransformationMatrix(AffineTransform at) { currentState.push(new CTM(UnitConv.ptToMpt(at))); diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index 2212da434..68e2e3c62 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -545,6 +545,16 @@ public class XMLRenderer extends AbstractXMLRenderer { //only necessary for graphical output } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + //only necessary for graphical output + } + + /** {@inheritDoc} */ + protected void endLayer() { + //only necessary for graphical output + } + /** * {@inheritDoc} * org.apache.fop.area.inline.InlineArea) diff --git a/src/java/org/apache/fop/util/AbstractPaintingState.java b/src/java/org/apache/fop/util/AbstractPaintingState.java index 96c3633e6..3e966d202 100644 --- a/src/java/org/apache/fop/util/AbstractPaintingState.java +++ b/src/java/org/apache/fop/util/AbstractPaintingState.java @@ -24,7 +24,6 @@ import java.awt.geom.AffineTransform; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.Stack; @@ -36,10 +35,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { private static final long serialVersionUID = 5998356138437094188L; /** current state data */ - private AbstractData data = null; + private AbstractData data; /** the state stack */ - private StateStack/**/ stateStack = new StateStack/**/(); + private StateStack stateStack = new StateStack(); /** * Instantiates a new state data object @@ -216,8 +215,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { */ public AffineTransform getTransform() { AffineTransform at = new AffineTransform(); - for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - AbstractData data = (AbstractData)iter.next(); + for (AbstractData data : stateStack) { AffineTransform stackTrans = data.getTransform(); at.concatenate(stackTrans); } @@ -249,7 +247,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { if (stateStack.isEmpty()) { return null; } else { - AbstractData baseData = (AbstractData)stateStack.get(0); + AbstractData baseData = stateStack.get(0); return (AffineTransform) baseData.getTransform().clone(); } } @@ -297,7 +295,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { */ public AbstractData restore() { if (!stateStack.isEmpty()) { - setData((AbstractData)stateStack.pop()); + setData(stateStack.pop()); return this.data; } else { return null; @@ -310,12 +308,11 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @param dataList a state data list */ - public void saveAll(List/**/ dataList) { - Iterator it = dataList.iterator(); - while (it.hasNext()) { + public void saveAll(List dataList) { + for (AbstractData data : dataList) { // save current data on stack save(); - setData((AbstractData)it.next()); + setData(data); } } @@ -325,8 +322,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @return a list of state data popped from the stack */ - public List/**/ restoreAll() { - List/**/ dataList = new java.util.ArrayList/**/(); + public List restoreAll() { + List dataList = new java.util.ArrayList(); AbstractData data; while (true) { data = getData(); @@ -361,7 +358,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @return the state stack */ - protected Stack/**/ getStateStack() { + protected Stack getStateStack() { return this.stateStack; } @@ -369,8 +366,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { @Override public Object clone() { AbstractPaintingState state = instantiate(); - state.stateStack = new StateStack(this.stateStack); - state.data = (AbstractData)this.data.clone(); + state.stateStack = new StateStack(this.stateStack); + if (this.data != null) { + state.data = (AbstractData)this.data.clone(); + } return state; } @@ -385,7 +384,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { /** * A stack implementation which holds state objects */ - public class StateStack extends java.util.Stack { + public class StateStack extends java.util.Stack { private static final long serialVersionUID = 4897178211223823041L; @@ -393,7 +392,6 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * Default constructor */ public StateStack() { - super(); } /** @@ -419,25 +417,28 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { private static final long serialVersionUID = 5208418041189828624L; /** The current color */ - protected Color color = null; + protected Color color; /** The current background color */ - protected Color backColor = null; + protected Color backColor; /** The current font name */ - protected String fontName = null; + protected String fontName; /** The current font size */ - protected int fontSize = 0; + protected int fontSize; /** The current line width */ - protected float lineWidth = 0; + protected float lineWidth; /** The dash array for the current basic stroke (line type) */ - protected float[] dashArray = null; + protected float[] dashArray; /** The current transform */ - protected AffineTransform transform = null; + protected AffineTransform transform; + + /** The current (optional content group) layer. */ + protected String layer; /** * Returns a newly create data object @@ -485,6 +486,18 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { transform = new AffineTransform(); } + public void setLayer(String layer) { + if (layer != null) { + this.layer = layer; + } else { + throw new IllegalArgumentException(); + } + } + + public String getLayer() { + return this.layer; + } + /** * Returns the derived rotation from the current transform * @@ -523,6 +536,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { this.transform = new AffineTransform(); } data.transform = new AffineTransform(this.transform); + data.layer = this.layer; return data; } @@ -535,7 +549,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { + ", fontSize=" + fontSize + ", lineWidth=" + lineWidth + ", dashArray=" + dashArray - + ", transform=" + transform; + + ", transform=" + transform + + ", layer=" + layer; } } } diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java index ebd143a69..fb7ce2677 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java @@ -146,16 +146,16 @@ public class SVGPainter extends AbstractIFPainter } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms), layer); } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - startGroup(SVGUtil.formatAffineTransformMptToPt(transform)); + public void startGroup(AffineTransform transform, String layer) throws IFException { + startGroup(SVGUtil.formatAffineTransformMptToPt(transform), layer); } - private void startGroup(String transform) throws IFException { + private void startGroup(String transform, String layer) throws IFException { try { AttributesImpl atts = new AttributesImpl(); if (transform != null && transform.length() > 0) { diff --git a/status.xml b/status.xml index 352f487c3..91b9c78d3 100644 --- a/status.xml +++ b/status.xml @@ -58,7 +58,10 @@ users, i.e. when the behaviour changes and could affect the layout of existing documents. Example: the fix of marks layering will be such a case when it's done. --> - + + + Enable support for PDF sub-page transitions. + Fix misplaced table cell border in WM RTL context. diff --git a/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java b/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java index d156b908c..ee3498a03 100644 --- a/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java +++ b/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java @@ -35,6 +35,8 @@ import static org.junit.Assert.assertTrue; */ public class EmbedFontInfoTestCase { + public EmbedFontInfoTestCase() {} + private EmbedFontInfo sut; private final URI metricsURI = URI.create("test/resources/fonts/ttf/glb12.ttf.xml"); diff --git a/test/java/org/apache/fop/fonts/FontsTestSuite.java b/test/java/org/apache/fop/fonts/FontsTestSuite.java index ad48c46c4..1baba49df 100644 --- a/test/java/org/apache/fop/fonts/FontsTestSuite.java +++ b/test/java/org/apache/fop/fonts/FontsTestSuite.java @@ -29,7 +29,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ FontManagerConfiguratorTestCase.class, - EmbedFontInfo.class, + EmbedFontInfoTestCase.class, FontEventProcessingTestCase.class, FontManagerConfiguratorTestCase.class }) diff --git a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java index 08d841ede..49c1e6dab 100644 --- a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java @@ -32,7 +32,7 @@ import java.util.List; public class PDFDestsTestCase extends PDFObjectTestCase { private PDFDests dests = new PDFDests(); - private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>\n"; + private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>"; @Before public void setUp() { diff --git a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java index 3f84fac2e..00224e93e 100644 --- a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java @@ -47,7 +47,7 @@ public class PDFDictionaryTestCase extends PDFObjectTestCase { + " /array [1 (two) 20]\n" + " /number 20\n" + " /null null\n" - + ">>\n"; + + ">>"; @Before public void setUp() { diff --git a/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java b/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java index e982bf246..a8be7ee74 100644 --- a/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java @@ -37,11 +37,11 @@ public class PDFPageLabelsTestCase { int index = 0; StringBuilder expected = new StringBuilder(); expected.append("["); - expected.append(index + " << /S /r >>\n"); + expected.append(index + " << /S /r >>"); pageLabels.addPageLabel(index++, "i"); pageLabels.addPageLabel(index++, "ii"); pageLabels.addPageLabel(index++, "iii"); - expected.append(" " + index + " << /S /D >>\n"); + expected.append(" " + index + " << /S /D >>"); pageLabels.addPageLabel(index++, "1"); pageLabels.addPageLabel(index++, "2"); pageLabels.addPageLabel(index++, "3"); @@ -52,33 +52,33 @@ public class PDFPageLabelsTestCase { pageLabels.addPageLabel(index++, "8"); pageLabels.addPageLabel(index++, "9"); pageLabels.addPageLabel(index++, "10"); - expected.append(" " + index + " << /S /A >>\n"); + expected.append(" " + index + " << /S /A >>"); pageLabels.addPageLabel(index++, "A"); pageLabels.addPageLabel(index++, "B"); - expected.append(" " + index + " << /S /R /St 100 >>\n"); + expected.append(" " + index + " << /S /R /St 100 >>"); pageLabels.addPageLabel(index++, "C"); - expected.append(" " + index + " << /S /R /St 500 >>\n"); + expected.append(" " + index + " << /S /R /St 500 >>"); pageLabels.addPageLabel(index++, "D"); - expected.append(" " + index + " << /S /A /St 5 >>\n"); + expected.append(" " + index + " << /S /A /St 5 >>"); pageLabels.addPageLabel(index++, "E"); pageLabels.addPageLabel(index++, "F"); pageLabels.addPageLabel(index++, "G"); - expected.append(" " + index + " << /P (aa) >>\n"); + expected.append(" " + index + " << /P (aa) >>"); pageLabels.addPageLabel(index++, "aa"); - expected.append(" " + index + " << /P (ab) >>\n"); + expected.append(" " + index + " << /P (ab) >>"); pageLabels.addPageLabel(index++, "ab"); - expected.append(" " + index + " << /P (ac) >>\n"); + expected.append(" " + index + " << /P (ac) >>"); pageLabels.addPageLabel(index++, "ac"); - expected.append(" " + index + " << /S /a >>\n"); + expected.append(" " + index + " << /S /a >>"); pageLabels.addPageLabel(index++, "a"); pageLabels.addPageLabel(index++, "b"); - expected.append(" " + index + " << /S /R /St 2 >>\n"); + expected.append(" " + index + " << /S /R /St 2 >>"); pageLabels.addPageLabel(index++, "II"); - expected.append(" " + index + " << /S /R /St 12 >>\n"); + expected.append(" " + index + " << /S /R /St 12 >>"); pageLabels.addPageLabel(index++, "XII"); - expected.append(" " + index + " <<\n /P (00)\n /S /D\n /St 9\n>>\n"); + expected.append(" " + index + " <<\n /P (00)\n /S /D\n /St 9\n>>"); pageLabels.addPageLabel(index++, "009"); - expected.append(" " + index + " <<\n /P (0)\n /S /D\n /St 10\n>>\n"); + expected.append(" " + index + " <<\n /P (0)\n /S /D\n /St 10\n>>"); pageLabels.addPageLabel(index++, "010"); pageLabels.addPageLabel(index++, "011"); expected.append("]"); diff --git a/test/java/org/apache/fop/pdf/PDFStreamTestCase.java b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java index 93dcea511..20e38a600 100644 --- a/test/java/org/apache/fop/pdf/PDFStreamTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java @@ -118,7 +118,7 @@ public class PDFStreamTestCase { private byte[] createSampleStreamData() throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); - stream.write("stream\n".getBytes("US-ASCII")); + stream.write("\nstream\n".getBytes("US-ASCII")); stream.write(createSampleData()); stream.write("\nendstream".getBytes("US-ASCII")); return stream.toByteArray(); diff --git a/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java index ceff96a91..12f6e3c1b 100644 --- a/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java +++ b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java @@ -73,7 +73,7 @@ public class CrossReferenceTableTestCase extends CrossReferenceObjectTest { .append(" /Info 2 0 R\n") .append(" /ID [<0123456789ABCDEF> <0123456789ABCDEF>]\n") .append(" /Size ").append(Integer.toString(offsets.size() + 1)).append('\n') - .append(">>\n"); + .append(">>"); return getBytes(expected); } diff --git a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java index 592335648..be8b5d718 100644 --- a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java +++ b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java @@ -51,7 +51,7 @@ public class AbstractIFPainterTestCase { public void endViewport() throws IFException { } - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { } public void endGroup() throws IFException { diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml new file mode 100644 index 000000000..486b8601c --- /dev/null +++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml @@ -0,0 +1,74 @@ + + + + + +

+ This test checks the PDF dictionary extensions related to optional content groups (layers). +

+
+ + + + + + + + + + + + + Bullet 1 + + + Bullet 2 + + + + + + + + + + + + Default + OFF + + + + + + + + BULLET 1A + BULLET 1B + + BULLET 2 + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml new file mode 100644 index 000000000..ee5061497 --- /dev/null +++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml @@ -0,0 +1,122 @@ + + + + + +

+ This test checks the PDF dictionary extensions related to optional content groups (layers), + including navigator and action dictionaries. +

+
+ + + + + + + + + + + + + + + + + Bullet 1 + + + Bullet 2 + + + + + OFF + + + + + + + OFF + + ON + + + + + + OFF + + ON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + OFF + + + + + + + + BULLET 1A + BULLET 1B + + BULLET 2 + + + + + + + + +
-- cgit v1.2.3 From 6714301a9c568f357ed749ade4f0796dfe8142e7 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Fri, 1 Nov 2013 20:58:48 +0000 Subject: FOP-2311: revert changes from FOP-1069 that regresses shorthand property handling. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1538062 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/properties/GenericShorthandParser.java | 3 ++- status.xml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java index ac3d4d13a..5c7bf4177 100644 --- a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java +++ b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java @@ -88,7 +88,8 @@ public class GenericShorthandParser implements ShorthandParser { while (iprop.hasNext() && prop == null) { Property p = (Property)iprop.next(); prop = maker.convertShorthandProperty(propertyList, p, null); - propertyList.validatePropertyValue(p.getNCname(), prop, property); + // The following produces a regression, about which see FOP-2311. + // propertyList.validatePropertyValue(p.getNCname(), prop, property); } return prop; } diff --git a/status.xml b/status.xml index 91b9c78d3..a54bab8e6 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Eliminate regression to shorthand properties from Bugzilla 37114 (FOP-1069). + Enable support for PDF sub-page transitions. -- cgit v1.2.3 From a00a96986a4aa5d2e904431cb35d98cea4e653f7 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 5 Nov 2013 12:08:10 +0000 Subject: FOP-1749: Fix offset of footnote when it has a margin-top Patch by Alexey Neyman, applied with minor modifications git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1538961 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/area/Footnote.java | 2 +- .../standard-testcases/footnote_jira1749.xml | 56 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/layoutengine/standard-testcases/footnote_jira1749.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/area/Footnote.java b/src/java/org/apache/fop/area/Footnote.java index bc9f27b24..28f3ee523 100644 --- a/src/java/org/apache/fop/area/Footnote.java +++ b/src/java/org/apache/fop/area/Footnote.java @@ -83,7 +83,7 @@ public class Footnote extends BlockParent { @Override public void addBlock(Block child) { addChildArea(child); - this.setBPD(this.getBPD() + child.getBPD()); + setBPD(getBPD() + child.getAllocBPD()); } } diff --git a/test/layoutengine/standard-testcases/footnote_jira1749.xml b/test/layoutengine/standard-testcases/footnote_jira1749.xml new file mode 100644 index 000000000..6e73d39fd --- /dev/null +++ b/test/layoutengine/standard-testcases/footnote_jira1749.xml @@ -0,0 +1,56 @@ + + + + + +

+ FOP-1749: footnotes with margin-top="..." that do not fit on the current + page, but would've fit without top margin cause FOP to hang (instead of + being pushed to the next page). Also, verify the top-offset for the + footnote is calculated properly with margin-top. +

+
+ + + + + + + + + + No footnote + Footnote call + * + + The footnote + + + + + + + + + + + + + + +
-- cgit v1.2.3 From fa1cca233c52222efbfee2a8e406df7c70daa1ac Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 5 Nov 2013 13:15:21 +0000 Subject: Reverted changes to PDFStructureTreeBuilder from rev. 1537600 git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1538984 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 3cc7a1017..985974b80 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -358,12 +358,8 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { } public StructureTreeElement startNode(String name, Attributes attributes, StructureTreeElement parent) { - PDFStructElem parentElem; - if ((parent != null) && (parent instanceof PDFStructElem)) { - parentElem = (PDFStructElem) parent; - } else { - parentElem = ancestors.getFirst(); - } + assert parent == null || parent instanceof PDFStructElem; + PDFStructElem parentElem = parent == null ? ancestors.getFirst() : (PDFStructElem) parent; PDFStructElem structElem = createStructureElement(name, parentElem, attributes, pdfFactory, eventBroadcaster); ancestors.addFirst(structElem); -- cgit v1.2.3 From 3e04d9245827bc5bcbd320728fb14ec6f7613290 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 6 Nov 2013 19:52:38 +0000 Subject: Added support for default alignment of inline-container areas with their parent areas git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539441 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 8 +- .../fop/layoutmgr/AbstractLayoutManager.java | 28 ++++ .../fop/layoutmgr/BlockContainerLayoutManager.java | 62 ++------- .../apache/fop/layoutmgr/BlockLayoutManager.java | 62 ++------- .../org/apache/fop/layoutmgr/LayoutManager.java | 17 +++ .../SpacedBorderedPaddedBlockLayoutManager.java | 112 +++++++++++++++ .../fop/layoutmgr/inline/ContentLayoutManager.java | 9 ++ .../inline/InlineContainerLayoutManager.java | 22 +-- .../fop/layoutmgr/inline/LineLayoutManager.java | 22 ++- .../fop/layoutmgr/list/ListBlockLayoutManager.java | 65 ++------- .../fop/layoutmgr/list/ListItemLayoutManager.java | 84 ++++------- .../fop/layoutmgr/table/TableLayoutManager.java | 68 ++------- .../inline-container_alignment.xml | 154 +++++++++++++++++++++ 13 files changed, 417 insertions(+), 296 deletions(-) create mode 100644 src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index d3a80eb3f..13ea9943a 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -29,7 +29,6 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -59,7 +58,6 @@ public class InlineContainer extends FObj { /** used for FO validation */ private boolean blockItemFound; - private CommonFont commonFont; /** * Creates a new instance. @@ -73,7 +71,6 @@ public class InlineContainer extends FObj { @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); - commonFont = pList.getFontProps(); // TODO get directly from parent? alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); @@ -214,8 +211,9 @@ public class InlineContainer extends FObj { return false; } - public CommonFont getCommonFont() { - return commonFont; + @Override + public boolean generatesReferenceAreas() { + return true; } } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index 0285a41e7..2dd18e4ee 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -342,6 +342,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im && isFinished()); } + public boolean hasLineAreaDescendant() { + if (childLMs == null || childLMs.isEmpty()) { + return false; + } else { + for (LayoutManager childLM : childLMs) { + if (childLM.hasLineAreaDescendant()) { + return true; + } + } + } + return false; + } + + public int getBaselineOffset() { + if (childLMs != null) { + for (LayoutManager childLM : childLMs) { + if (childLM.hasLineAreaDescendant()) { + return childLM.getBaselineOffset(); + } + } + } + throw newNoLineAreaDescendantException(); + } + + protected IllegalStateException newNoLineAreaDescendantException() { + return new IllegalStateException("getBaselineOffset called on an object that has no line-area descendant"); + } + /** * Transfers foreign attributes from the formatting object to the area. * @param targetArea the area to set the attributes on diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index a23cd28f1..83990797b 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -37,6 +37,7 @@ import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.properties.CommonAbsolutePosition; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal; /** * LayoutManager for a block-container FO. */ -public class BlockContainerLayoutManager extends BlockStackingLayoutManager implements - ConditionalElementListener, BreakOpportunity { +public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** * logging instance @@ -79,13 +80,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl private MinOptMax foBlockSpaceBefore; private MinOptMax foBlockSpaceAfter; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private int horizontalOverflow; private double contentRectOffsetX = 0; private double contentRectOffsetY = 0; @@ -128,6 +122,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getBlockContainerFO().getCommonBorderPaddingBackground(); + } + private void resetSpaces() { this.discardBorderBefore = false; this.discardBorderAfter = false; @@ -993,51 +992,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl return true; } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ public boolean handleOverflow(int milliPoints) { if (milliPoints > this.horizontalOverflow) { diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 0fb738aea..4129b65bd 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -32,6 +32,7 @@ import org.apache.fop.area.Block; import org.apache.fop.area.LineArea; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; @@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal; /** * LayoutManager for a block FO. */ -public class BlockLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener, - BreakOpportunity { +public class BlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** logging instance */ private static Log log = LogFactory.getLog(BlockLayoutManager.class); @@ -60,13 +61,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co private int follow = 2000; //private int middleShift = 0; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - /** * Creates a new BlockLayoutManager. * @param inBlock the block FO object to create the layout manager for. @@ -100,6 +94,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co .getOptimum(this).getLength().getValue(this); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getBlockFO().getCommonBorderPaddingBackground(); + } + /** {@inheritDoc} */ @Override public List getNextKnuthElements(LayoutContext context, int alignment) { @@ -456,51 +455,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co return true; } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ @Override public boolean isRestartable() { diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java index 985131bf1..107a252b0 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java @@ -177,6 +177,23 @@ public interface LayoutManager extends PercentBaseContext { */ List getChangedKnuthElements(List oldList, int alignment); + /** + * Whether the FO handled by this layout manager had a descendant (including itself) + * that will generate a line-area. + * + * @return {@code true} if a descendant line-area will be generated, {@code false} otherwise + */ + boolean hasLineAreaDescendant(); + + /** + * Returns the position of the dominant-baseline of this FO's first descendant + * line-area, if any. + * + * @return this FO's space-before plus the distance from the before-edge of its + * allocation-rectangle to the dominant-baseline of the first line-area descendant + */ + int getBaselineOffset(); + /** * Returns the IPD of the content area * @return the IPD of the content area diff --git a/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java new file mode 100644 index 000000000..2ac41e96a --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java @@ -0,0 +1,112 @@ +/* + * 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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.FObj; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.traits.MinOptMax; + +/** + * A block-stacking layout manager for an FO that supports spaces, border and padding. + */ +public abstract class SpacedBorderedPaddedBlockLayoutManager extends BlockStackingLayoutManager + implements ConditionalElementListener { + + private static final Log LOG = LogFactory.getLog(BlockLayoutManager.class); + + protected MinOptMax effSpaceBefore; + + protected MinOptMax effSpaceAfter; + + protected boolean discardBorderBefore; + protected boolean discardBorderAfter; + protected boolean discardPaddingBefore; + protected boolean discardPaddingAfter; + + public SpacedBorderedPaddedBlockLayoutManager(FObj node) { + super(node); + } + + public void notifySpace(RelSide side, MinOptMax effectiveLength) { + if (RelSide.BEFORE == side) { + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Space " + side + ", " + + this.effSpaceBefore + "-> " + effectiveLength); + } + this.effSpaceBefore = effectiveLength; + } else { + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Space " + side + ", " + + this.effSpaceAfter + "-> " + effectiveLength); + } + this.effSpaceAfter = effectiveLength; + } + } + + public void notifyBorder(RelSide side, MinOptMax effectiveLength) { + if (effectiveLength == null) { + if (RelSide.BEFORE == side) { + this.discardBorderBefore = true; + } else { + this.discardBorderAfter = true; + } + } + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Border " + side + " -> " + effectiveLength); + } + } + + public void notifyPadding(RelSide side, MinOptMax effectiveLength) { + if (effectiveLength == null) { + if (RelSide.BEFORE == side) { + this.discardPaddingBefore = true; + } else { + this.discardPaddingAfter = true; + } + } + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Padding " + side + " -> " + effectiveLength); + } + } + + @Override + public int getBaselineOffset() { + int baselineOffset = super.getBaselineOffset(); + if (effSpaceBefore != null) { + baselineOffset += effSpaceBefore.getOpt(); + } + if (!discardBorderBefore) { + baselineOffset += getCommonBorderPaddingBackground().getBorderBeforeWidth(false); + } + if (!discardPaddingBefore) { + baselineOffset += getCommonBorderPaddingBackground().getPaddingBefore(false, this); + } + return baselineOffset; + } + + /** + * Returns the {@link CommonBorderPaddingBackground} instance from the FO handled by this layout manager. + */ + protected abstract CommonBorderPaddingBackground getCommonBorderPaddingBackground(); + +} diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java index b3c768987..c067b040f 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java @@ -332,6 +332,15 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager return parentLM.getPSLM(); } + + public boolean hasLineAreaDescendant() { + return true; + } + + public int getBaselineOffset() { + return childLM.getBaselineOffset(); + } + // --------- Property Resolution related functions --------- // /** diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 75eb12d5b..9495e2c73 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -31,9 +31,6 @@ import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.fo.properties.Property; -import org.apache.fop.fonts.Font; -import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelEventProducer; @@ -64,6 +61,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private List childElements; private int ipdOverflow; + private AlignmentContext alignmentContext; private InlineViewport currentViewport; private Container referenceArea; @@ -84,7 +82,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen childLC.setRefIPD(contentAreaIPD); childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? determineBPD(); - AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? + alignmentContext = makeAlignmentContext(context); // TODO correct? Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); @@ -121,6 +119,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); + SpaceResolver.performConditionalsNotification(allChildElements, 0, allChildElements.size() - 1, -1); // TODO break-before, break-after return allChildElements; } @@ -149,13 +148,13 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen protected AlignmentContext makeAlignmentContext(LayoutContext context) { InlineContainer ic = (InlineContainer) fobj; - FontInfo fi = fobj.getFOEventHandler().getFontInfo(); - FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); - Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(contentAreaBPD, + AlignmentContext ac = new AlignmentContext(contentAreaBPD, ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); + int baselineOffset = hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + ac.resizeLine(contentAreaBPD, baselineOffset); + return ac; } private void handleIPDOverflow() { @@ -183,6 +182,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; } + @Override + public boolean getGeneratesReferenceArea() { + return true; + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -196,7 +200,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen assert inlineContainerPosition.getLM() == this; } assert inlineContainerPosition != null; - SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); AreaAdditionUtil.addAreas(this, childPosIter, context); @@ -212,6 +215,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen TraitSetter.setProducerID(referenceArea, fobj.getId()); referenceArea.setIPD(contentAreaIPD); currentViewport = new InlineViewport(referenceArea); + currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); currentViewport.setIPD(getContentAreaIPD()); currentViewport.setBPD(getContentAreaBPD()); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index b3987a075..25d8c0872 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -160,6 +160,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager private final int follow; private AlignmentContext alignmentContext; + private int baselineOffset = -1; + private List knuthParagraphs; private LineLayoutPossibilities lineLayouts; @@ -556,7 +558,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager private int constantLineHeight = 12000; - /** * Create a new Line Layout Manager. * This is used by the block layout manager to create @@ -939,7 +940,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager while (listIter.hasNext()) { ListElement tempElement; tempElement = (ListElement) listIter.next(); - if (tempElement.getLayoutManager() != this) { + LayoutManager lm = tempElement.getLayoutManager(); + if (baselineOffset < 0 && lm != null && lm.hasLineAreaDescendant()) { + baselineOffset = lm.getBaselineOffset(); + } + if (lm != this) { tempElement.setPosition(notifyPos(new NonLeafPosition(this, tempElement.getPosition()))); } @@ -987,6 +992,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager } startIndex = endIndex + 1; LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i); + if (baselineOffset < 0) { + baselineOffset = lbp.spaceBefore + lbp.baseline; + } returnList.add(new KnuthBlockBox( lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, footnoteList, lbp, false)); @@ -1424,6 +1432,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } + @Override + public boolean hasLineAreaDescendant() { + return true; + } + + @Override + public int getBaselineOffset() { + return baselineOffset; + } + /** * Add the areas with the break points. * diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 61d8a891d..44a9720a4 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -28,16 +28,15 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.fo.flow.ListBlock; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; -import org.apache.fop.layoutmgr.BlockStackingLayoutManager; -import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.RelSide; +import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -47,21 +46,13 @@ import org.apache.fop.traits.SpaceVal; * A list block contains list items which are stacked within * the list block area.. */ -public class ListBlockLayoutManager extends BlockStackingLayoutManager - implements ConditionalElementListener { +public class ListBlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager { /** logging instance */ private static Log log = LogFactory.getLog(ListBlockLayoutManager.class); private Block curBlockArea; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - /** * Create a new list block layout manager. * @param node list-block to create the layout manager for @@ -70,6 +61,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager super(node); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getListBlockFO().getCommonBorderPaddingBackground(); + } + /** * Convenience method. * @return the ListBlock node @@ -277,50 +273,5 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager return getListBlockFO().getKeepWithNext(); } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 083e4ee1b..344f6722b 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -32,12 +32,11 @@ import org.apache.fop.area.Block; 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.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; -import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.BreakOpportunity; import org.apache.fop.layoutmgr.BreakOpportunityHelper; -import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager; @@ -53,10 +52,9 @@ import org.apache.fop.layoutmgr.ListElement; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.RelSide; import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; -import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; import org.apache.fop.util.BreakUtil; @@ -64,8 +62,8 @@ import org.apache.fop.util.BreakUtil; * LayoutManager for a list-item FO. * The list item contains a list item label and a list item body. */ -public class ListItemLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener, - BreakOpportunity { +public class ListItemLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** logging instance */ private static Log log = LogFactory.getLog(ListItemLayoutManager.class); @@ -78,13 +76,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements private List labelList = null; private List bodyList = null; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private Keep keepWithNextPendingOnLabel; private Keep keepWithNextPendingOnBody; @@ -145,6 +136,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements setBody(node.getBody()); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getListItemFO().getCommonBorderPaddingBackground(); + } + /** * Convenience method. * @return the ListBlock node @@ -475,6 +471,23 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements return returnedList; } + + @Override + public boolean hasLineAreaDescendant() { + return label.hasLineAreaDescendant() || body.hasLineAreaDescendant(); + } + + @Override + public int getBaselineOffset() { + if (label.hasLineAreaDescendant()) { + return label.getBaselineOffset(); + } else if (body.hasLineAreaDescendant()) { + return body.getBaselineOffset(); + } else { + throw newNoLineAreaDescendantException(); + } + } + /** * Add the areas for the break points. * @@ -652,51 +665,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements return getListItemFO().getKeepWithNext(); } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ @Override public void reset() { diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 7f1754064..afb6547c0 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -39,12 +39,11 @@ 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.CommonBorderPaddingBackground; 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; import org.apache.fop.layoutmgr.LayoutContext; @@ -52,7 +51,7 @@ import org.apache.fop.layoutmgr.LeafPosition; import org.apache.fop.layoutmgr.ListElement; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.RelSide; +import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -66,8 +65,8 @@ import org.apache.fop.util.BreakUtil; * The table then creates areas for the columns, bodies and rows * the render background. */ -public class TableLayoutManager extends BlockStackingLayoutManager - implements ConditionalElementListener, BreakOpportunity { +public class TableLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** * logging instance @@ -82,13 +81,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager private double tableUnit; private boolean autoLayout = true; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private int halfBorderSeparationBPD; private int halfBorderSeparationIPD; @@ -132,6 +124,13 @@ public class TableLayoutManager extends BlockStackingLayoutManager this.columns = new ColumnSetup(node); } + + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getTable().getCommonBorderPaddingBackground(); + } + + /** @return the table FO */ public Table getTable() { return (Table)this.fobj; @@ -521,51 +520,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ public void reset() { super.reset(); diff --git a/test/layoutengine/standard-testcases/inline-container_alignment.xml b/test/layoutengine/standard-testcases/inline-container_alignment.xml new file mode 100644 index 000000000..153d9a9c4 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment.xml @@ -0,0 +1,154 @@ + + + + + +

+ Checks that the inline-container is properly aligned with the parent area. +

+
+ + + + + + + + + + By default the alignment is with the baseline of the first descendant + line-area. + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + The line before. The line before. Before: + + + Inside the + inline-container. + + + After the inline-container. + + + + + + This inline-container has no line-area descendant. The after edge of its + allocation rectangle should be aligned with the baseline. + The line before. The line before. Before: + + After the inline-container. + + + + + + The first line-area descendant is in fo:list-item-body. + The line before. The line before. Before: + + + + + + + + List item + + + + + After the inline-container. + + + + + + This inline-container contains a block that contains an inline that contains a + block. + The line before. The line before. Before: + inline + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 89b34550c9bb72ba8dcf01e09038ad11ec5757f6 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:27:22 +0000 Subject: Added support for non-default values of alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539610 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 43 ++++- .../inline-container_alignment-adjust.xml | 210 +++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 9495e2c73..626ce8209 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -27,6 +27,8 @@ import org.apache.fop.area.Area; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; +import org.apache.fop.datatypes.Length; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -152,7 +154,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); - int baselineOffset = hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + int baselineOffset = getAlignmentPoint(); ac.resizeLine(contentAreaBPD, baselineOffset); return ac; } @@ -182,6 +184,45 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; } + private int getAlignmentPoint() { + Length alignmentAdjust = ((InlineContainer) fobj).getAlignmentAdjust(); + int baseline = alignmentAdjust.getEnum(); + if (baseline == Constants.EN_AUTO + || baseline == Constants.EN_BASELINE) { + return getInlineContainerBaselineOffset(alignmentBaseline); + } else if (baseline != 0) { + return getInlineContainerBaselineOffset(baseline); + } else { + return 0; + } + } + + private int getInlineContainerBaselineOffset(int property) { + switch (property) { + case Constants.EN_BEFORE_EDGE: + case Constants.EN_TEXT_BEFORE_EDGE: + return 0; + case Constants.EN_AFTER_EDGE: + case Constants.EN_TEXT_AFTER_EDGE: + return contentAreaBPD; + case Constants.EN_MIDDLE: + case Constants.EN_CENTRAL: + case Constants.EN_MATHEMATICAL: + return contentAreaBPD / 2; + case Constants.EN_IDEOGRAPHIC: + return contentAreaBPD * 7 / 10; + case Constants.EN_ALPHABETIC: + return contentAreaBPD * 6 / 10; + case Constants.EN_HANGING: + return contentAreaBPD * 2 / 10; + case Constants.EN_AUTO: + case Constants.EN_BASELINE: + return hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + default: + throw new AssertionError("Unknown baseline value: " + property); + } + } + @Override public boolean getGeneratesReferenceArea() { return true; diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml new file mode 100644 index 000000000..917a3a1d2 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -0,0 +1,210 @@ + + + + + +

+ Checks that the alignment-adjust property on inline-container behaves properly. +

+
+ + + + + + + + + + + alignment-adjust="before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="text-before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="after-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="text-after-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="middle": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="ideographic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="alphabetic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="hanging": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="mathematical": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 6e65f409038594b64900780044fc5e94a84504e3 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:29:17 +0000 Subject: Added support for alignment-baseline on fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539611 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 20 +++--- .../inline-container_alignment-baseline.xml | 74 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 626ce8209..b043268bd 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -57,7 +57,6 @@ import org.apache.fop.layoutmgr.TraitSetter; public class InlineContainerLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager { private CommonBorderPaddingBackground borderProps; - private int alignmentBaseline = EN_BASELINE; private int contentAreaIPD; private int contentAreaBPD; @@ -77,6 +76,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen borderProps = node.getCommonBorderPaddingBackground(); } + private InlineContainer getInlineContainer() { + assert fobj instanceof InlineContainer; + return (InlineContainer) fobj; + } + @Override public List getNextKnuthElements(LayoutContext context, int alignment) { determineIPD(context); @@ -95,7 +99,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private void determineIPD(LayoutContext layoutContext) { - LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); + LengthRangeProperty ipd = getInlineContainer().getInlineProgressionDimension(); Property optimum = ipd.getOptimum(this); if (optimum.isAuto()) { contentAreaIPD = layoutContext.getRefIPD(); @@ -114,7 +118,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? childLC.setRefIPD(layoutContext.getRefIPD()); @SuppressWarnings("unchecked") - List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + List childElements = childLM.getNextKnuthElements(childLC, alignment); allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } @@ -127,7 +131,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private void determineBPD() { - LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); + LengthRangeProperty bpd = getInlineContainer().getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { @@ -181,15 +185,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private boolean canRecoverFromOverflow() { - return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + return getInlineContainer().getOverflow() != EN_ERROR_IF_OVERFLOW; } private int getAlignmentPoint() { - Length alignmentAdjust = ((InlineContainer) fobj).getAlignmentAdjust(); + Length alignmentAdjust = getInlineContainer().getAlignmentAdjust(); int baseline = alignmentAdjust.getEnum(); if (baseline == Constants.EN_AUTO || baseline == Constants.EN_BASELINE) { - return getInlineContainerBaselineOffset(alignmentBaseline); + return getInlineContainerBaselineOffset(getInlineContainer().getAlignmentBaseline()); } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { @@ -294,7 +298,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private boolean needClip() { - int overflow = ((InlineContainer) fobj).getOverflow(); + int overflow = getInlineContainer().getOverflow(); return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml b/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml new file mode 100644 index 000000000..53b9be0a9 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml @@ -0,0 +1,74 @@ + + + + + +

+ Checks that the alignment-baseline property on inline-container behaves properly. +

+
+ + + + + + + + + + + alignment-baseline="before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-baseline="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From e1ee53b4c48a7f7781fe88a0336e77dad0df37cd Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:29:56 +0000 Subject: Improved javadoc for getBaselineOffset git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539612 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/layoutmgr/LayoutManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java index 107a252b0..8d1e4001c 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java @@ -178,7 +178,7 @@ public interface LayoutManager extends PercentBaseContext { List getChangedKnuthElements(List oldList, int alignment); /** - * Whether the FO handled by this layout manager had a descendant (including itself) + * Whether the FO handled by this layout manager has a descendant (including itself) * that will generate a line-area. * * @return {@code true} if a descendant line-area will be generated, {@code false} otherwise @@ -187,10 +187,12 @@ public interface LayoutManager extends PercentBaseContext { /** * Returns the position of the dominant-baseline of this FO's first descendant - * line-area, if any. + * line-area.

The behavior of this method is undefined if this FO has no descendant + * line-area, and an exception may be thrown. See {@link #hasLineAreaDescendant()}

* * @return this FO's space-before plus the distance from the before-edge of its * allocation-rectangle to the dominant-baseline of the first line-area descendant + * @see #hasLineAreaDescendant() */ int getBaselineOffset(); -- cgit v1.2.3 From d08e7a0a304e7f3e3e0d5181bfc00959aa493eee Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:31:31 +0000 Subject: Added support for dominant-baseline on fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539613 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/AlignmentContext.java | 2 +- .../inline/InlineContainerLayoutManager.java | 9 +-- .../inline-container_dominant-baseline.xml | 74 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java index c1992965c..192956abc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java @@ -295,7 +295,7 @@ public class AlignmentContext implements Constants { * Return the dominant baseline identifier. * @return the dominant baseline identifier */ - private int getDominantBaselineIdentifier() { + public int getDominantBaselineIdentifier() { return actualBaselineTable.getDominantBaselineIdentifier(); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index b043268bd..21e3cb283 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -158,7 +158,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); - int baselineOffset = getAlignmentPoint(); + int baselineOffset = getAlignmentPoint(ac.getDominantBaselineIdentifier()); ac.resizeLine(contentAreaBPD, baselineOffset); return ac; } @@ -188,12 +188,13 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return getInlineContainer().getOverflow() != EN_ERROR_IF_OVERFLOW; } - private int getAlignmentPoint() { + private int getAlignmentPoint(int dominantBaseline) { Length alignmentAdjust = getInlineContainer().getAlignmentAdjust(); int baseline = alignmentAdjust.getEnum(); - if (baseline == Constants.EN_AUTO - || baseline == Constants.EN_BASELINE) { + if (baseline == Constants.EN_AUTO) { return getInlineContainerBaselineOffset(getInlineContainer().getAlignmentBaseline()); + } else if (baseline == Constants.EN_BASELINE) { + return getInlineContainerBaselineOffset(dominantBaseline); } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { diff --git a/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml b/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml new file mode 100644 index 000000000..402eb806d --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml @@ -0,0 +1,74 @@ + + + + + +

+ Checks that the dominant-baseline property on inline-container behaves properly. +

+
+ + + + + + + + + + + dominant-baseline="alphabetic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + dominant-baseline="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From f4bb629ab722bbc4991cd4e5ceb37fb0a060b613 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:32:20 +0000 Subject: Added support for values on alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539614 13f79535-47bb-0310-9956-ffa450edef68 --- .../layoutmgr/inline/InlineContainerLayoutManager.java | 7 ++++++- .../inline-container_alignment-adjust.xml | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 21e3cb283..539ebc897 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -28,6 +28,8 @@ import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; import org.apache.fop.datatypes.Length; +import org.apache.fop.datatypes.LengthBase; +import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; @@ -198,7 +200,10 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { - return 0; + int baselineOffset = getInlineContainerBaselineOffset(dominantBaseline); + int adjust = alignmentAdjust.getValue( + new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, 0)); + return baselineOffset + adjust; } } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml index 917a3a1d2..b63e36b69 100644 --- a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -131,6 +131,16 @@ + + + alignment-adjust="30pt": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + @@ -206,5 +216,12 @@ + + + + + + +
-- cgit v1.2.3 From d1ac3aa05b692e9d9ead48b93e8e9e9ad4297416 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:33:11 +0000 Subject: Added support for values on alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539615 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 3 ++- .../inline-container_alignment-adjust.xml | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 539ebc897..5127794c0 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -201,8 +201,9 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return getInlineContainerBaselineOffset(baseline); } else { int baselineOffset = getInlineContainerBaselineOffset(dominantBaseline); + int lineHeight = getInlineContainer().getLineHeight().getOptimum(this).getLength().getValue(this); int adjust = alignmentAdjust.getValue( - new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, 0)); + new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, lineHeight)); return baselineOffset + adjust; } } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml index b63e36b69..3e696a696 100644 --- a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -141,6 +141,16 @@ + + + alignment-adjust="10%": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + @@ -221,7 +231,14 @@ - + + + + + + + +
-- cgit v1.2.3 From 7d3dba6c3c2fc6caeeeaccbc8df159b87934e44a Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Wed, 13 Nov 2013 15:24:26 +0000 Subject: FOP-2313: add support for svg gradients when generating PostScript; most code authored by Robert Meyer with a small contribution by Athanasios Giannimaras. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1541551 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 1 + lib/xmlgraphics-commons-svn-trunk.jar | Bin 629507 -> 629955 bytes src/java/org/apache/fop/pdf/PDFFactory.java | 106 ++-- src/java/org/apache/fop/pdf/PDFFunction.java | 651 ++++----------------- src/java/org/apache/fop/pdf/PDFPattern.java | 14 +- src/java/org/apache/fop/pdf/PDFShading.java | 373 ++++++------ .../apache/fop/render/ps/PSImageHandlerSVG.java | 327 +++++++++-- .../org/apache/fop/render/ps/svg/PSFunction.java | 143 +++++ .../org/apache/fop/render/ps/svg/PSPattern.java | 103 ++++ .../apache/fop/render/ps/svg/PSSVGGraphics2D.java | 291 +++++++++ .../org/apache/fop/render/ps/svg/PSShading.java | 228 ++++++++ .../org/apache/fop/render/shading/Function.java | 39 ++ .../fop/render/shading/FunctionDelegate.java | 451 ++++++++++++++ .../apache/fop/render/shading/FunctionPattern.java | 363 ++++++++++++ .../apache/fop/render/shading/GradientFactory.java | 162 +++++ .../fop/render/shading/GradientRegistrar.java | 45 ++ .../fop/render/shading/PDFGradientFactory.java | 76 +++ .../fop/render/shading/PSGradientFactory.java | 70 +++ .../org/apache/fop/render/shading/Pattern.java | 22 + .../org/apache/fop/render/shading/Shading.java | 26 + .../apache/fop/render/shading/ShadingPattern.java | 105 ++++ src/java/org/apache/fop/svg/PDFGraphics2D.java | 62 +- .../fop/render/ps/svg/PSSVGGraphics2DTestCase.java | 71 +++ .../ps/svg/PSSVGLinearGraphics2DTestCase.java | 70 +++ .../fop/render/ps/svg/axial-shading-expected.dat | 26 + test/java/org/apache/fop/render/ps/svg/expected.ps | 38 ++ 26 files changed, 3009 insertions(+), 854 deletions(-) create mode 100644 src/java/org/apache/fop/render/ps/svg/PSFunction.java create mode 100644 src/java/org/apache/fop/render/ps/svg/PSPattern.java create mode 100644 src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java create mode 100644 src/java/org/apache/fop/render/ps/svg/PSShading.java create mode 100644 src/java/org/apache/fop/render/shading/Function.java create mode 100644 src/java/org/apache/fop/render/shading/FunctionDelegate.java create mode 100644 src/java/org/apache/fop/render/shading/FunctionPattern.java create mode 100644 src/java/org/apache/fop/render/shading/GradientFactory.java create mode 100644 src/java/org/apache/fop/render/shading/GradientRegistrar.java create mode 100644 src/java/org/apache/fop/render/shading/PDFGradientFactory.java create mode 100644 src/java/org/apache/fop/render/shading/PSGradientFactory.java create mode 100644 src/java/org/apache/fop/render/shading/Pattern.java create mode 100644 src/java/org/apache/fop/render/shading/Shading.java create mode 100644 src/java/org/apache/fop/render/shading/ShadingPattern.java create mode 100644 test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java create mode 100644 test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java create mode 100644 test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat create mode 100644 test/java/org/apache/fop/render/ps/svg/expected.ps (limited to 'src/java/org/apache/fop') diff --git a/build.xml b/build.xml index 97503f208..f25239bc0 100644 --- a/build.xml +++ b/build.xml @@ -579,6 +579,7 @@ list of possible build targets. + diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar index 22b0f5d52..fad312940 100644 Binary files a/lib/xmlgraphics-commons-svn-trunk.jar and b/lib/xmlgraphics-commons-svn-trunk.jar differ diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 5af33866e..070630274 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -310,12 +310,7 @@ public class PDFFactory { theFunctionDataStream, theFilter); - PDFFunction oldfunc = getDocument().findFunction(function); - if (oldfunc == null) { - getDocument().registerObject(function); - } else { - function = oldfunc; - } + function = registerFunction(function); return (function); } @@ -352,12 +347,7 @@ public class PDFFactory { PDFFunction function = new PDFFunction(theFunctionType, theDomain, theRange, theCZero, theCOne, theInterpolationExponentN); - PDFFunction oldfunc = getDocument().findFunction(function); - if (oldfunc == null) { - getDocument().registerObject(function); - } else { - function = oldfunc; - } + function = registerFunction(function); return (function); } @@ -407,12 +397,7 @@ public class PDFFactory { theRange, theFunctions, theBounds, theEncode); - PDFFunction oldfunc = getDocument().findFunction(function); - if (oldfunc == null) { - getDocument().registerObject(function); - } else { - function = oldfunc; - } + function = registerFunction(function); return (function); } @@ -434,14 +419,23 @@ public class PDFFactory { theRange, theFunctionDataStream); + function = registerFunction(function); + return (function); + + } + + /** + * Registers a function against the document + * @param function The function to register + */ + public PDFFunction registerFunction(PDFFunction function) { PDFFunction oldfunc = getDocument().findFunction(function); if (oldfunc == null) { getDocument().registerObject(function); } else { function = oldfunc; } - return (function); - + return function; } /* ========================= shadings ================================== */ @@ -481,20 +475,7 @@ public class PDFFactory { theBBox, theAntiAlias, theDomain, theMatrix, theFunction); - PDFShading oldshad = getDocument().findShading(shading); - if (oldshad == null) { - getDocument().registerObject(shading); - } else { - shading = oldshad; - } - - // add this shading to resources - if (res != null) { - res.getPDFResources().addShading(shading); - } else { - getDocument().getResources().addShading(shading); - } - + shading = registerShading(res, shading); return (shading); } @@ -534,18 +515,7 @@ public class PDFFactory { theDomain, theFunction, theExtend); - PDFShading oldshad = getDocument().findShading(shading); - if (oldshad == null) { - getDocument().registerObject(shading); - } else { - shading = oldshad; - } - - if (res != null) { - res.getPDFResources().addShading(shading); - } else { - getDocument().getResources().addShading(shading); - } + shading = registerShading(res, shading); return (shading); } @@ -591,18 +561,7 @@ public class PDFFactory { theBitsPerFlag, theDecode, theFunction); - PDFShading oldshad = getDocument().findShading(shading); - if (oldshad == null) { - getDocument().registerObject(shading); - } else { - shading = oldshad; - } - - if (res != null) { - res.getPDFResources().addShading(shading); - } else { - getDocument().getResources().addShading(shading); - } + shading = registerShading(res, shading); return (shading); } @@ -645,6 +604,17 @@ public class PDFFactory { theBitsPerComponent, theDecode, theVerticesPerRow, theFunction); + shading = registerShading(res, shading); + + return (shading); + } + + /** + * Registers a shading object against the document + * @param res The PDF resource context + * @param shading The shading object to be registered + */ + public PDFShading registerShading(PDFResourceContext res, PDFShading shading) { PDFShading oldshad = getDocument().findShading(shading); if (oldshad == null) { getDocument().registerObject(shading); @@ -652,13 +622,13 @@ public class PDFFactory { shading = oldshad; } + // add this shading to resources if (res != null) { res.getPDFResources().addShading(shading); } else { getDocument().getResources().addShading(shading); } - - return (shading); + return shading; } /* ========================= patterns ================================== */ @@ -707,6 +677,22 @@ public class PDFFactory { return (pattern); } + public PDFPattern registerPattern(PDFResourceContext res, PDFPattern pattern) { + PDFPattern oldpatt = getDocument().findPattern(pattern); + if (oldpatt == null) { + getDocument().registerObject(pattern); + } else { + pattern = oldpatt; + } + + if (res != null) { + res.getPDFResources().addPattern(pattern); + } else { + getDocument().getResources().addPattern(pattern); + } + return pattern; + } + /** * Make a smooth shading pattern * diff --git a/src/java/org/apache/fop/pdf/PDFFunction.java b/src/java/org/apache/fop/pdf/PDFFunction.java index f424569b9..09cbd9708 100644 --- a/src/java/org/apache/fop/pdf/PDFFunction.java +++ b/src/java/org/apache/fop/pdf/PDFFunction.java @@ -22,6 +22,10 @@ package org.apache.fop.pdf; // Java... import java.util.List; +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.FunctionDelegate; +import org.apache.fop.render.shading.FunctionPattern; + /** * class representing a PDF Function. * @@ -33,126 +37,9 @@ import java.util.List; * * All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range. */ -public class PDFFunction extends PDFObject { - // Guts common to all function types - - /** - * Required: The Type of function (0,2,3,4) default is 0. - */ - protected int functionType = 0; // Default - - /** - * Required: 2 * m Array of Double numbers which are possible inputs to the function - */ - protected List domain = null; - - /** - * Required: 2 * n Array of Double numbers which are possible outputs to the function - */ - protected List range = null; - - /* ********************TYPE 0***************************** */ - // FunctionType 0 specific function guts - - /** - * Required: Array containing the Integer size of the Domain and Range, respectively. - * Note: This is really more like two seperate integers, sizeDomain, and sizeRange, - * but since they're expressed as an array in PDF, my implementation reflects that. - */ - protected List size = null; - - /** - * Required for Type 0: Number of Bits used to represent each sample value. - * Limited to 1,2,4,8,12,16,24, or 32 - */ - protected int bitsPerSample = 1; - - /** - * Optional for Type 0: order of interpolation between samples. - * Limited to linear (1) or cubic (3). Default is 1 - */ - protected int order = 1; - - /** - * Optional for Type 0: A 2 * m array of Doubles which provides a - * linear mapping of input values to the domain. - * - * Required for Type 3: A 2 * k array of Doubles that, taken - * in pairs, map each subset of the domain defined by Domain - * and the Bounds array to the domain of the corresponding function. - * Should be two values per function, usually (0,1), - * as in [0 1 0 1] for 2 functions. - */ - protected List encode = null; - - /** - * Optional for Type 0: A 2 * n array of Doubles which provides - * a linear mapping of sample values to the range. Defaults to Range. - */ - protected List decode = null; - - /** - * Optional For Type 0: A stream of sample values - */ - - /** - * Required For Type 4: Postscript Calculator function - * composed of arithmetic, boolean, and stack operators + boolean constants - */ - protected StringBuffer functionDataStream = null; - - /** - * Required (possibly) For Type 0: A vector of Strings for the - * various filters to be used to decode the stream. - * These are how the string is compressed. Flate, LZW, etc. - */ - protected List filter = null; - /* *************************TYPE 2************************** */ - - /** - * Required For Type 2: An Array of n Doubles defining - * the function result when x=0. Default is [0]. - */ - protected List cZero = null; - - /** - * Required For Type 2: An Array of n Doubles defining - * the function result when x=1. Default is [1]. - */ - protected List cOne = null; +public class PDFFunction extends PDFObject implements Function { - /** - * Required for Type 2: The interpolation exponent. - * Each value x will return n results. - * Must be greater than 0. - */ - protected double interpolationExponentN = 1; - - /* *************************TYPE 3************************** */ - - /** - * Required for Type 3: An vector of PDFFunctions which - * form an array of k single input functions making up - * the stitching function. - */ - protected List functions = null; - - /** - * Optional for Type 3: An array of (k-1) Doubles that, - * in combination with Domain, define the intervals to which - * each function from the Functions array apply. Bounds - * elements must be in order of increasing magnitude, - * and each value must be within the value of Domain. - * k is the number of functions. - * If you pass null, it will output (1/k) in an array of k-1 elements. - * This makes each function responsible for an equal amount of the stitching function. - * It makes the gradient even. - */ - protected List bounds = null; - // See encode above, as it's also part of Type 3 Functions. - - /* *************************TYPE 4************************** */ - // See 'data' above. + private FunctionDelegate delegate; /** * create an complete Function object of Type 0, A Sampled function. @@ -211,26 +98,13 @@ public class PDFFunction extends PDFObject { * @param theFunctionType This is the type of function (0,2,3, or 4). * It should be 0 as this is the constructor for sampled functions. */ - public PDFFunction(int theFunctionType, List theDomain, - List theRange, List theSize, int theBitsPerSample, - int theOrder, List theEncode, List theDecode, - StringBuffer theFunctionDataStream, List theFilter) { - super(); - - this.functionType = 0; // dang well better be 0; - this.size = theSize; - this.bitsPerSample = theBitsPerSample; - this.order = theOrder; // int - this.encode = theEncode; // vector of int - this.decode = theDecode; // vector of int - this.functionDataStream = theFunctionDataStream; - this.filter = theFilter; // vector of Strings - - // the domain and range are actually two dimensional arrays. - // so if there's not an even number of items, bad stuff - // happens. - this.domain = theDomain; - this.range = theRange; + public PDFFunction(int theFunctionType, List theDomain, + List theRange, List theSize, int theBitsPerSample, + int theOrder, List theEncode, List theDecode, + StringBuffer theFunctionDataStream, List theFilter) { + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, + theSize, theBitsPerSample, theOrder, theEncode, theDecode, + theFunctionDataStream, theFilter); } /** @@ -260,20 +134,11 @@ public class PDFFunction extends PDFObject { * PDF Spec page 268 * @param theFunctionType The type of the function, which should be 2. */ - public PDFFunction(int theFunctionType, List theDomain, - List theRange, List theCZero, List theCOne, + public PDFFunction(int theFunctionType, List theDomain, + List theRange, List theCZero, List theCOne, double theInterpolationExponentN) { - super(); - - this.functionType = 2; // dang well better be 2; - - this.cZero = theCZero; - this.cOne = theCOne; - this.interpolationExponentN = theInterpolationExponentN; - - - this.domain = theDomain; - this.range = theRange; + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, + theCZero, theCOne, theInterpolationExponentN); } @@ -312,18 +177,11 @@ public class PDFFunction extends PDFObject { * @param theFunctionType This is the function type. It should be 3, * for a stitching function. */ - public PDFFunction(int theFunctionType, List theDomain, - List theRange, List theFunctions, - List theBounds, List theEncode) { - super(); - - this.functionType = 3; // dang well better be 3; - - this.functions = theFunctions; - this.bounds = theBounds; - this.encode = theEncode; - this.domain = theDomain; - this.range = theRange; + public PDFFunction(int theFunctionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode) { + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, + theFunctions, theBounds, theEncode); } @@ -349,20 +207,12 @@ public class PDFFunction extends PDFObject { * @param theFunctionType The type of function which should be 4, as this is * a Postscript calculator function */ - public PDFFunction(int theFunctionType, List theDomain, - List theRange, StringBuffer theFunctionDataStream) { - super(); - - this.functionType = 4; // dang well better be 4; - this.functionDataStream = theFunctionDataStream; - - this.domain = theDomain; - - this.range = theRange; - + public PDFFunction(int theFunctionType, List theDomain, + List theRange, StringBuffer theFunctionDataStream) { + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, + theFunctionDataStream); } - /** * represent as PDF. Whatever the FunctionType is, the correct * representation spits out. The sets of required and optional @@ -375,319 +225,13 @@ public class PDFFunction extends PDFObject { * @return the PDF string. */ public byte[] toPDF() { - int vectorSize = 0; - int numberOfFunctions = 0; - int tempInt = 0; - StringBuffer p = new StringBuffer(256); - p.append("<< \n/FunctionType " + this.functionType + " \n"); - - // FunctionType 0 - if (this.functionType == 0) { - if (this.domain != null) { - // DOMAIN - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - // SIZE - if (this.size != null) { - p.append("/Size [ "); - vectorSize = this.size.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.size.get(tempInt)) - + " "); - } - p.append("] \n"); - } - // ENCODE - if (this.encode != null) { - p.append("/Encode [ "); - vectorSize = this.encode.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt)) - + " "); - } - p.append("] \n"); - } else { - p.append("/Encode [ "); - vectorSize = this.functions.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append("0 1 "); - } - p.append("] \n"); - - } - - // BITSPERSAMPLE - p.append("/BitsPerSample " + this.bitsPerSample); - - // ORDER (optional) - if (this.order == 1 || this.order == 3) { - p.append(" \n/Order " + this.order + " \n"); - } - - // RANGE - if (this.range != null) { - p.append("/Range [ "); - vectorSize = this.range.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) - + " "); - } - - p.append("] \n"); - } - - // DECODE - if (this.decode != null) { - p.append("/Decode [ "); - vectorSize = this.decode.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.decode.get(tempInt)) - + " "); - } - - p.append("] \n"); - } - - // LENGTH - if (this.functionDataStream != null) { - p.append("/Length " + (this.functionDataStream.length() + 1) - + " \n"); - } - - // FILTER? - if (this.filter != null) { // if there's a filter - vectorSize = this.filter.size(); - p.append("/Filter "); - if (vectorSize == 1) { - p.append("/" + ((String)this.filter.get(0)) - + " \n"); - } else { - p.append("[ "); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append("/" + ((String)this.filter.get(0)) - + " "); - } - p.append("] \n"); - } - } - p.append(">>"); - - // stream representing the function - if (this.functionDataStream != null) { - p.append("\nstream\n" + this.functionDataStream - + "\nendstream"); - } - - // end of if FunctionType 0 - - } else if (this.functionType == 2) { - // DOMAIN - if (this.domain != null) { - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - - // RANGE - if (this.range != null) { - p.append("/Range [ "); - vectorSize = this.range.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) - + " "); - } - - p.append("] \n"); - } - - // FunctionType, C0, C1, N are required in PDF - - // C0 - if (this.cZero != null) { - p.append("/C0 [ "); - vectorSize = this.cZero.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.cZero.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - // C1 - if (this.cOne != null) { - p.append("/C1 [ "); - vectorSize = this.cOne.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.cOne.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - // N: The interpolation Exponent - p.append("/N " - + PDFNumber.doubleOut(new Double(this.interpolationExponentN)) - + " \n"); - - p.append(">>"); - - } else if (this.functionType - == 3) { // fix this up when my eyes uncross - // DOMAIN - if (this.domain != null) { - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - // RANGE - if (this.range != null) { - p.append("/Range [ "); - vectorSize = this.range.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) - + " "); - } - - p.append("] \n"); - } - - // FUNCTIONS - if (this.functions != null) { - p.append("/Functions [ "); - numberOfFunctions = this.functions.size(); - for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) { - p.append(((PDFFunction)this.functions.get(tempInt)).referencePDF() - + " "); - - } - p.append("] \n"); - } - - - // ENCODE - if (this.encode != null) { - p.append("/Encode [ "); - vectorSize = this.encode.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt)) - + " "); - } - - p.append("] \n"); - } else { - p.append("/Encode [ "); - vectorSize = this.functions.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append("0 1 "); - } - p.append("] \n"); - - } - - - // BOUNDS, required, but can be empty - p.append("/Bounds [ "); - if (this.bounds != null) { - - vectorSize = this.bounds.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.bounds.get(tempInt)) - + " "); - } - - } else { - if (this.functions != null) { - // if there are n functions, - // there must be n-1 bounds. - // so let each function handle an equal portion - // of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ] - - String functionsFraction = PDFNumber.doubleOut(new Double(1.0 - / ((double)numberOfFunctions))); - - for (tempInt = 0; tempInt + 1 < numberOfFunctions; - tempInt++) { - - p.append(functionsFraction + " "); - } - functionsFraction = null; // clean reference. - - } - - } - p.append("]\n>>"); - } else if (this.functionType - == 4) { // fix this up when my eyes uncross - // DOMAIN - if (this.domain != null) { - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - // RANGE - if (this.range != null) { - p.append("/Range [ "); - vectorSize = this.range.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt)) - + " "); - } - - p.append("] \n"); - } - - // LENGTH - if (this.functionDataStream != null) { - p.append("/Length " + (this.functionDataStream.length() + 1) - + " \n"); - } - - p.append(">>"); - - // stream representing the function - if (this.functionDataStream != null) { - p.append("\nstream\n{ " + this.functionDataStream - + " }\nendstream"); - } - - - } + return toByteString(); + } - return encode(p.toString()); + public byte[] toByteString() { + FunctionPattern pattern = new FunctionPattern(this); + return encode(pattern.toWriteableString()); } /** {@inheritDoc} */ @@ -702,96 +246,155 @@ public class PDFFunction extends PDFObject { return false; } PDFFunction func = (PDFFunction)obj; - if (functionType != func.functionType) { + if (delegate.getFunctionType() != func.getFunctionType()) { return false; } - if (bitsPerSample != func.bitsPerSample) { + if (delegate.getBitsPerSample() != func.getBitsPerSample()) { return false; } - if (order != func.order) { + if (delegate.getOrder() != func.getOrder()) { return false; } - if (interpolationExponentN != func.interpolationExponentN) { + if (delegate.getInterpolationExponentN() != func.getInterpolationExponentN()) { return false; } - if (domain != null) { - if (!domain.equals(func.domain)) { + if (delegate.getDomain() != null) { + if (!delegate.getDomain().equals(func.getDomain())) { return false; } - } else if (func.domain != null) { + } else if (func.getDomain() != null) { return false; } - if (range != null) { - if (!range.equals(func.range)) { + if (delegate.getRange() != null) { + if (!delegate.getRange().equals(func.getRange())) { return false; } - } else if (func.range != null) { + } else if (func.getRange() != null) { return false; } - if (size != null) { - if (!size.equals(func.size)) { + if (delegate.getSize() != null) { + if (!delegate.getSize().equals(func.getSize())) { return false; } - } else if (func.size != null) { + } else if (func.getSize() != null) { return false; } - if (encode != null) { - if (!encode.equals(func.encode)) { + if (delegate.getEncode() != null) { + if (!delegate.getEncode().equals(func.getEncode())) { return false; } - } else if (func.encode != null) { + } else if (func.getEncode() != null) { return false; } - if (decode != null) { - if (!decode.equals(func.decode)) { + if (delegate.getDecode() != null) { + if (!delegate.getDecode().equals(func.getDecode())) { return false; } - } else if (func.decode != null) { + } else if (func.getDecode() != null) { return false; } - if (functionDataStream != null) { - if (!functionDataStream.equals(func.functionDataStream)) { + if (delegate.getDataStream() != null) { + if (!delegate.getDataStream().equals(func.getDataStream())) { return false; } - } else if (func.functionDataStream != null) { + } else if (func.getDataStream() != null) { return false; } - if (filter != null) { - if (!filter.equals(func.filter)) { + if (delegate.getFilter() != null) { + if (!delegate.getFilter().equals(func.getFilter())) { return false; } - } else if (func.filter != null) { + } else if (func.getFilter() != null) { return false; } - if (cZero != null) { - if (!cZero.equals(func.cZero)) { + if (delegate.getCZero() != null) { + if (!delegate.getCZero().equals(func.getCZero())) { return false; } - } else if (func.cZero != null) { + } else if (func.getCZero() != null) { return false; } - if (cOne != null) { - if (!cOne.equals(func.cOne)) { + if (delegate.getCOne() != null) { + if (!delegate.getCOne().equals(func.getCOne())) { return false; } - } else if (func.cOne != null) { + } else if (func.getCOne() != null) { return false; } - if (functions != null) { - if (!functions.equals(func.functions)) { + if (delegate.getFunctions() != null) { + if (!delegate.getFunctions().equals(func.getFunctions())) { return false; } - } else if (func.functions != null) { + } else if (func.getFunctions() != null) { return false; } - if (bounds != null) { - if (!bounds.equals(func.bounds)) { + if (delegate.getBounds() != null) { + if (!delegate.getBounds().equals(func.getBounds())) { return false; } - } else if (func.bounds != null) { + } else if (func.getBounds() != null) { return false; } return true; } + public int getFunctionType() { + return delegate.getFunctionType(); + } + + public List getBounds() { + return delegate.getBounds(); + } + + public List getDomain() { + return delegate.getDomain(); + } + + public List getSize() { + return delegate.getSize(); + } + + public List getFilter() { + return delegate.getFilter(); + } + + public List getEncode() { + return delegate.getEncode(); + } + + public List getFunctions() { + return delegate.getFunctions(); + } + + public int getBitsPerSample() { + return delegate.getBitsPerSample(); + } + + public double getInterpolationExponentN() { + return delegate.getInterpolationExponentN(); + } + + public int getOrder() { + return delegate.getOrder(); + } + + public List getRange() { + return delegate.getRange(); + } + + public List getDecode() { + return delegate.getDecode(); + } + + public StringBuffer getDataStream() { + return delegate.getDataStream(); + } + + public List getCZero() { + return delegate.getCZero(); + } + + public List getCOne() { + return delegate.getCOne(); + } } diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java index b9e042815..df4b0233d 100644 --- a/src/java/org/apache/fop/pdf/PDFPattern.java +++ b/src/java/org/apache/fop/pdf/PDFPattern.java @@ -23,6 +23,9 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; +import org.apache.fop.render.shading.Pattern; +import org.apache.fop.render.shading.Shading; + /** * class representing a PDF Function. * @@ -33,7 +36,7 @@ import java.util.List; * * All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range. */ -public class PDFPattern extends PDFPathPaint { +public class PDFPattern extends PDFPathPaint implements Pattern { /** * The resources associated with this pattern @@ -146,13 +149,14 @@ public class PDFPattern extends PDFPathPaint { * @param theExtGState optional: the extended graphics state, if used. * @param theMatrix Optional:List of Doubles that specify the matrix. */ - public PDFPattern(int thePatternType, PDFShading theShading, + public PDFPattern(int thePatternType, Shading theShading, List theXUID, StringBuffer theExtGState, List theMatrix) { super(); this.patternType = 2; // thePatternType; - this.shading = theShading; + assert theShading instanceof PDFShading; + this.shading = (PDFShading)theShading; this.xUID = theXUID; // this isn't really implemented, so it should always be null. // I just don't want to have to add a new parameter once it is implemented. @@ -259,7 +263,7 @@ public class PDFPattern extends PDFPathPaint { vectorSize = this.xUID.size(); p.append("/XUID [ "); for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(((Integer)this.xUID.get(tempInt)) + " "); + p.append((this.xUID.get(tempInt)) + " "); } p.append("] \n"); } @@ -290,7 +294,7 @@ public class PDFPattern extends PDFPathPaint { vectorSize = this.xUID.size(); p.append("/XUID [ "); for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(((Integer)this.xUID.get(tempInt)) + " "); + p.append((this.xUID.get(tempInt)) + " "); } p.append("] \n"); } diff --git a/src/java/org/apache/fop/pdf/PDFShading.java b/src/java/org/apache/fop/pdf/PDFShading.java index 62012b9b2..3f7b2b4b0 100644 --- a/src/java/org/apache/fop/pdf/PDFShading.java +++ b/src/java/org/apache/fop/pdf/PDFShading.java @@ -22,6 +22,10 @@ package org.apache.fop.pdf; // Java... import java.util.List; +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.Shading; +import org.apache.fop.render.shading.ShadingPattern; + /** * class representing a PDF Smooth Shading object. * @@ -32,7 +36,7 @@ import java.util.List; * * All PDF Functions have a shadingType (0,2,3, or 4), a Domain, and a Range. */ -public class PDFShading extends PDFObject { +public class PDFShading extends PDFObject implements Shading { // Guts common to all function types /** @@ -205,7 +209,7 @@ public class PDFShading extends PDFObject { public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace, List theBackground, List theBBox, boolean theAntiAlias, List theCoords, - List theDomain, PDFFunction theFunction, + List theDomain, Function theFunction, List theExtend) { super(); this.shadingType = theShadingType; // 2 or 3 @@ -216,7 +220,8 @@ public class PDFShading extends PDFObject { this.coords = theCoords; this.domain = theDomain; - this.function = theFunction; + assert theFunction instanceof PDFFunction; + this.function = (PDFFunction)theFunction; this.extend = theExtend; } @@ -335,197 +340,8 @@ public class PDFShading extends PDFObject { * @return the PDF string. */ public String toPDFString() { - int vectorSize; - int tempInt; - StringBuffer p = new StringBuffer(128); - p.append("<<\n/ShadingType " + this.shadingType + " \n"); - if (this.colorSpace != null) { - p.append("/ColorSpace /" - + this.colorSpace.getName() + " \n"); - } - - if (this.background != null) { - p.append("/Background [ "); - vectorSize = this.background.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.background.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - if (this.bBox - != null) { // I've never seen an example, so I guess this is right. - p.append("/BBox [ "); - vectorSize = this.bBox.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.bBox.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - if (this.antiAlias) { - p.append("/AntiAlias " + this.antiAlias + " \n"); - } - - // Here's where we differentiate based on what type it is. - if (this.shadingType == 1) { // function based shading - if (this.domain != null) { - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - if (this.matrix != null) { - p.append("/Matrix [ "); - vectorSize = this.matrix.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.matrix.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - if (this.function != null) { - p.append("/Function "); - p.append(this.function.referencePDF() + " \n"); - } - } else if ((this.shadingType == 2) - || (this.shadingType - == 3)) { // 2 is axial shading (linear gradient) - // 3 is radial shading (circular gradient) - if (this.coords != null) { - p.append("/Coords [ "); - vectorSize = this.coords.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.coords.get(tempInt)) - + " "); - } - p.append("] \n"); - } - - // DOMAIN - if (this.domain != null) { - p.append("/Domain [ "); - vectorSize = this.domain.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt)) - + " "); - } - p.append("] \n"); - } else { - p.append("/Domain [ 0 1 ] \n"); - } - - if (this.extend != null) { - p.append("/Extend [ "); - vectorSize = this.extend.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(((Boolean)this.extend.get(tempInt)) + " "); - } - - p.append("] \n"); - } else { - p.append("/Extend [ true true ] \n"); - } - - - if (this.function != null) { - p.append("/Function "); - p.append(this.function.referencePDF() + " \n"); - } - - - } else if ((this.shadingType == 4) || (this.shadingType == 6) - || (this.shadingType - == 7)) { // 4:Free-form Gouraud-shaded triangle meshes - // 6:coons patch meshes - // 7://tensor product patch meshes (which no one ever uses) - if (this.bitsPerCoordinate > 0) { - p.append("/BitsPerCoordinate " + this.bitsPerCoordinate - + " \n"); - } else { - p.append("/BitsPerCoordinate 1 \n"); - } - - if (this.bitsPerComponent > 0) { - p.append("/BitsPerComponent " + this.bitsPerComponent - + " \n"); - } else { - p.append("/BitsPerComponent 1 \n"); - } - - if (this.bitsPerFlag > 0) { - p.append("/BitsPerFlag " + this.bitsPerFlag + " \n"); - } else { - p.append("/BitsPerFlag 2 \n"); - } - - if (this.decode != null) { - p.append("/Decode [ "); - vectorSize = this.decode.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(((Boolean)this.decode.get(tempInt)) + " "); - } - - p.append("] \n"); - } - - if (this.function != null) { - p.append("/Function "); - p.append(this.function.referencePDF() + " \n"); - } - - } else if (this.shadingType - == 5) { // Lattice Free form gouraud-shaded triangle mesh - - if (this.bitsPerCoordinate > 0) { - p.append("/BitsPerCoordinate " + this.bitsPerCoordinate - + " \n"); - } else { - p.append("/BitsPerCoordinate 1 \n"); - } - - if (this.bitsPerComponent > 0) { - p.append("/BitsPerComponent " + this.bitsPerComponent - + " \n"); - } else { - p.append("/BitsPerComponent 1 \n"); - } - - if (this.decode != null) { - p.append("/Decode [ "); - vectorSize = this.decode.size(); - for (tempInt = 0; tempInt < vectorSize; tempInt++) { - p.append(((Boolean)this.decode.get(tempInt)) + " "); - } - - p.append("] \n"); - } - - if (this.function != null) { - p.append("/Function "); - p.append(this.function.referencePDF() + " \n"); - } - - if (this.verticesPerRow > 0) { - p.append("/VerticesPerRow " + this.verticesPerRow + " \n"); - } else { - p.append("/VerticesPerRow 2 \n"); - } - - } - - p.append(">>"); - - return (p.toString()); + ShadingPattern pattern = new ShadingPattern(this); + return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias); } /** {@inheritDoc} */ @@ -623,4 +439,173 @@ public class PDFShading extends PDFObject { } return true; } + + /** + * A method to write a type 1 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType1(StringBuffer p) { + if (this.domain != null) { + p.append("/Domain [ "); + for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { + p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) + + " "); + } + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + if (this.matrix != null) { + p.append("/Matrix [ "); + for (int matrixIndex = 0; matrixIndex < matrix.size(); matrixIndex++) { + p.append(PDFNumber.doubleOut((Double)this.matrix.get(matrixIndex)) + + " "); + } + p.append("] \n"); + } + + if (this.function != null) { + p.append("/Function "); + p.append(this.function.referencePDF() + " \n"); + } + return p; + } + + /** + * A method to write a type 2 or 3 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType2or3(StringBuffer p) { + // 3 is radial shading (circular gradient) + if (this.coords != null) { + p.append("/Coords [ "); + for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) { + p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex)) + + " "); + } + p.append("] \n"); + } + + // DOMAIN + if (this.domain != null) { + p.append("/Domain [ "); + for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { + p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) + + " "); + } + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + if (this.extend != null) { + p.append("/Extend [ "); + for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) { + p.append((this.extend.get(extendIndex)) + " "); + } + + p.append("] \n"); + } else { + p.append("/Extend [ true true ] \n"); + } + + + if (this.function != null) { + p.append("/Function "); + p.append(this.function.referencePDF() + " \n"); + } + + return p; + } + + /** + * A method to write a type 4, 6 or 7 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType4or6or7(StringBuffer p) { + // 6:coons patch meshes + // 7://tensor product patch meshes (which no one ever uses) + if (this.bitsPerCoordinate > 0) { + p.append("/BitsPerCoordinate " + this.bitsPerCoordinate + + " \n"); + } else { + p.append("/BitsPerCoordinate 1 \n"); + } + + if (this.bitsPerComponent > 0) { + p.append("/BitsPerComponent " + this.bitsPerComponent + + " \n"); + } else { + p.append("/BitsPerComponent 1 \n"); + } + + if (this.bitsPerFlag > 0) { + p.append("/BitsPerFlag " + this.bitsPerFlag + " \n"); + } else { + p.append("/BitsPerFlag 2 \n"); + } + + if (this.decode != null) { + p.append("/Decode [ "); + for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) { + p.append((this.decode.get(decodeIndex)) + " "); + } + + p.append("] \n"); + } + + if (this.function != null) { + p.append("/Function "); + p.append(this.function.referencePDF() + " \n"); + } + + return p; + } + + /** + * A method to write a type 5 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType5(StringBuffer p) { + if (this.bitsPerCoordinate > 0) { + p.append("/BitsPerCoordinate " + this.bitsPerCoordinate + + " \n"); + } else { + p.append("/BitsPerCoordinate 1 \n"); + } + + if (this.bitsPerComponent > 0) { + p.append("/BitsPerComponent " + this.bitsPerComponent + + " \n"); + } else { + p.append("/BitsPerComponent 1 \n"); + } + + if (this.decode != null) { + p.append("/Decode [ "); + for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) { + p.append((this.decode.get(decodeIndex)) + " "); + } + + p.append("] \n"); + } + + if (this.function != null) { + p.append("/Function "); + p.append(this.function.referencePDF() + " \n"); + } + + if (this.verticesPerRow > 0) { + p.append("/VerticesPerRow " + this.verticesPerRow + " \n"); + } else { + p.append("/VerticesPerRow 2 \n"); + } + + return p; + } } diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java index 3ade34522..cadc28267 100644 --- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java @@ -19,26 +19,50 @@ package org.apache.fop.render.ps; +import java.awt.Color; +import java.awt.Dimension; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.imageio.ImageIO; import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.transcoder.SVGAbstractTranscoder; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; -import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; +import org.apache.xmlgraphics.ps.ImageEncoder; +import org.apache.xmlgraphics.ps.ImageEncodingHelper; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.fop.image.loader.batik.BatikImageFlavors; import org.apache.fop.image.loader.batik.BatikUtil; import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RenderingContext; +import org.apache.fop.render.ps.svg.PSSVGGraphics2D; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; @@ -47,6 +71,9 @@ import org.apache.fop.svg.SVGUserAgent; */ public class PSImageHandlerSVG implements ImageHandler { + private static final Color FALLBACK_COLOR = new Color(255, 33, 117); + private HashMap gradientsFound = new HashMap(); + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { BatikImageFlavors.SVG_DOM }; @@ -58,78 +85,262 @@ public class PSImageHandlerSVG implements ImageHandler { PSGenerator gen = psContext.getGenerator(); ImageXMLDOM imageSVG = (ImageXMLDOM)image; - //Controls whether text painted by Batik is generated using text or path operations - boolean strokeText = false; - //TODO Configure text stroking + if (shouldRaster(imageSVG)) { + InputStream is = renderSVGToInputStream(context, imageSVG); + + float x = (float) pos.getX() / 1000f; + float y = (float) pos.getY() / 1000f; + float w = (float) pos.getWidth() / 1000f; + float h = (float) pos.getHeight() / 1000f; + Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h); + + MaskedImage mi = convertToRGB(ImageIO.read(is)); + BufferedImage ri = mi.getImage(); + ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(ri); + Dimension imgDim = new Dimension(ri.getWidth(), ri.getHeight()); + String imgDescription = ri.getClass().getName(); + ImageEncodingHelper helper = new ImageEncodingHelper(ri); + ColorModel cm = helper.getEncodedColorModel(); + PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen, ri, mi.getMaskColor()); + } else { + //Controls whether text painted by Batik is generated using text or path operations + boolean strokeText = false; + //TODO Configure text stroking + + SVGUserAgent ua + = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + + PSSVGGraphics2D graphics = new PSSVGGraphics2D(strokeText, gen); + graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); + + BridgeContext ctx = new PSBridgeContext(ua, + (strokeText ? null : psContext.getFontInfo()), + context.getUserAgent().getImageManager(), + context.getUserAgent().getImageSessionContext()); + + //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) + //to it. + Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); + + GraphicsNode root; + try { + GVTBuilder builder = new GVTBuilder(); + root = builder.build(ctx, clonedDoc); + } catch (Exception e) { + SVGEventProducer eventProducer = SVGEventProducer.Provider.get( + context.getUserAgent().getEventBroadcaster()); + eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI()); + return; + } + // get the 'width' and 'height' attributes of the SVG document + float w = (float)ctx.getDocumentSize().getWidth() * 1000f; + float h = (float)ctx.getDocumentSize().getHeight() * 1000f; - SVGUserAgent ua - = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + float sx = pos.width / w; + float sy = pos.height / h; - PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); - graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); + ctx = null; - BridgeContext ctx = new PSBridgeContext(ua, - (strokeText ? null : psContext.getFontInfo()), - context.getUserAgent().getImageManager(), - context.getUserAgent().getImageSessionContext()); + gen.commentln("%FOPBeginSVG"); + gen.saveGraphicsState(); + final boolean clip = false; + if (clip) { + /* + * Clip to the svg area. + * Note: To have the svg overlay (under) a text area then use + * an fo:block-container + */ + gen.writeln("newpath"); + gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f, + pos.width / 1000f, pos.height / 1000f); + gen.writeln("clip"); + } - //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) - //to it. - Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument()); + // transform so that the coordinates (0,0) is from the top left + // and positive is down and to the right. (0,0) is where the + // viewBox puts it. + gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f); - GraphicsNode root; + AffineTransform transform = new AffineTransform(); + // scale to viewbox + transform.translate(pos.getMinX(), pos.getMinY()); + gen.getCurrentState().concatMatrix(transform); + try { + root.paint(graphics); + } catch (Exception e) { + SVGEventProducer eventProducer = SVGEventProducer.Provider.get( + context.getUserAgent().getEventBroadcaster()); + eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); + } + + gen.restoreGraphicsState(); + gen.commentln("%FOPEndSVG"); + } + } + + private InputStream renderSVGToInputStream(RenderingContext context, ImageXMLDOM imageSVG) throws IOException { + PNGTranscoder png = new PNGTranscoder(); + Float width = getDimension(imageSVG.getDocument(), "width") * 8; + png.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, width); + Float height = getDimension(imageSVG.getDocument(), "height") * 8; + png.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, height); + TranscoderInput input = new TranscoderInput(imageSVG.getDocument()); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + TranscoderOutput output = new TranscoderOutput(os); try { - GVTBuilder builder = new GVTBuilder(); - root = builder.build(ctx, clonedDoc); - } catch (Exception e) { + png.transcode(input, output); + } catch (TranscoderException ex) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); - eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI()); - return; + eventProducer.svgRenderingError(this, ex, imageSVG.getInfo().getOriginalURI()); + } finally { + os.flush(); + os.close(); + } + return new ByteArrayInputStream(os.toByteArray()); + } + + private MaskedImage convertToRGB(BufferedImage alphaImage) { + int[] red = new int[256]; + int[] green = new int[256]; + int[] blue = new int[256]; + BufferedImage rgbImage = new BufferedImage(alphaImage.getWidth(), + alphaImage.getHeight(), BufferedImage.TYPE_INT_RGB); + //Count occurances of each colour in image + for (int cx = 0; cx < alphaImage.getWidth(); cx++) { + for (int cy = 0; cy < alphaImage.getHeight(); cy++) { + int pixelValue = alphaImage.getRGB(cx, cy); + Color pixelColor = new Color(pixelValue); + red[pixelColor.getRed()]++; + green[pixelColor.getGreen()]++; + blue[pixelColor.getBlue()]++; + } + } + //Find colour not in image + Color alphaSwap = null; + for (int i = 0; i < 256; i++) { + if (red[i] == 0) { + alphaSwap = new Color(i, 0, 0); + break; + } else if (green[i] == 0) { + alphaSwap = new Color(0, i, 0); + break; + } else if (blue[i] == 0) { + alphaSwap = new Color(0, 0, i); + break; + } } - // get the 'width' and 'height' attributes of the SVG document - float w = (float)ctx.getDocumentSize().getWidth() * 1000f; - float h = (float)ctx.getDocumentSize().getHeight() * 1000f; - - float sx = pos.width / w; - float sy = pos.height / h; - - ctx = null; - - gen.commentln("%FOPBeginSVG"); - gen.saveGraphicsState(); - final boolean clip = false; - if (clip) { - /* - * Clip to the svg area. - * Note: To have the svg overlay (under) a text area then use - * an fo:block-container - */ - gen.writeln("newpath"); - gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f, - pos.width / 1000f, pos.height / 1000f); - gen.writeln("clip"); + //Check if all variations are used in all three colours + if (alphaSwap == null) { + //Fallback colour is no unique colour channel can be found + alphaSwap = FALLBACK_COLOR; } + //Replace alpha channel with the new mask colour + for (int cx = 0; cx < alphaImage.getWidth(); cx++) { + for (int cy = 0; cy < alphaImage.getHeight(); cy++) { + int pixelValue = alphaImage.getRGB(cx, cy); + if (pixelValue == 0) { + rgbImage.setRGB(cx, cy, alphaSwap.getRGB()); + } else { + rgbImage.setRGB(cx, cy, alphaImage.getRGB(cx, cy)); + } + } + } + return new MaskedImage(rgbImage, alphaSwap); + } + + private static class MaskedImage { + private Color maskColor = new Color(0, 0, 0); + private BufferedImage image; + + public MaskedImage(BufferedImage image, Color maskColor) { + this.image = image; + this.maskColor = maskColor; + } + + public Color getMaskColor() { + return maskColor; + } + + public BufferedImage getImage() { + return image; + } + } - // transform so that the coordinates (0,0) is from the top left - // and positive is down and to the right. (0,0) is where the - // viewBox puts it. - gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f); + private Float getDimension(Document document, String dimension) { + if (document.getFirstChild().getAttributes().getNamedItem(dimension) != null) { + String width = document.getFirstChild().getAttributes().getNamedItem(dimension).getNodeValue(); + width = width.replaceAll("[^\\d.]", ""); + return Float.parseFloat(width); + } + return null; + } - AffineTransform transform = new AffineTransform(); - // scale to viewbox - transform.translate(pos.getMinX(), pos.getMinY()); - gen.getCurrentState().concatMatrix(transform); + private boolean shouldRaster(ImageXMLDOM image) { + //A list of objects on which to check opacity try { - root.paint(graphics); - } catch (Exception e) { - SVGEventProducer eventProducer = SVGEventProducer.Provider.get( - context.getUserAgent().getEventBroadcaster()); - eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); + List gradMatches = new ArrayList(); + gradMatches.add("radialGradient"); + gradMatches.add("linearGradient"); + return recurseSVGElements(image.getDocument().getChildNodes(), gradMatches, false); + } finally { + gradientsFound.clear(); } + } - gen.restoreGraphicsState(); - gen.commentln("%FOPEndSVG"); + private boolean recurseSVGElements(NodeList childNodes, List gradMatches, boolean isMatched) { + boolean opacityFound = false; + for (int i = 0; i < childNodes.getLength(); i++) { + Node curNode = childNodes.item(i); + if (isMatched && curNode.getLocalName() != null && curNode.getLocalName().equals("stop")) { + if (curNode.getAttributes().getNamedItem("style") != null) { + String[] stylePairs = curNode.getAttributes().getNamedItem("style").getNodeValue() + .split(";"); + for (int styleAtt = 0; styleAtt < stylePairs.length; styleAtt++) { + String[] style = stylePairs[styleAtt].split(":"); + if (style[0].equalsIgnoreCase("stop-opacity")) { + if (Double.parseDouble(style[1]) < 1) { + return true; + } + } + } + } + if (curNode.getAttributes().getNamedItem("stop-opacity") != null) { + String opacityValue = curNode.getAttributes().getNamedItem("stop-opacity").getNodeValue(); + if (Double.parseDouble(opacityValue) < 1) { + return true; + } + } + } + String nodeName = curNode.getLocalName(); + //Special case where rasterization needed for radial gradients + if (nodeName != null && nodeName.equals("ellipse")) { + String found = ""; + String ellipseFill = curNode.getAttributes().getNamedItem("fill").getNodeValue(); + Pattern pattern = Pattern.compile("#(.*?)\\)"); + Matcher matcher = pattern.matcher(ellipseFill); + if (matcher.find()) { + found = matcher.group(1); + } + if (gradientsFound.get(found) != null) { + return true; + } + } + boolean inMatch = false; + if (!isMatched) { + inMatch = nodeName != null && gradMatches.contains(nodeName); + if (inMatch) { + gradientsFound.put(curNode.getAttributes().getNamedItem("id").getNodeValue(), nodeName); + } + } else { + inMatch = true; + } + opacityFound = recurseSVGElements(curNode.getChildNodes(), gradMatches, inMatch); + if (opacityFound) { + return true; + } + } + return opacityFound; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/ps/svg/PSFunction.java b/src/java/org/apache/fop/render/ps/svg/PSFunction.java new file mode 100644 index 000000000..b03e0b590 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/svg/PSFunction.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps.svg; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.FunctionDelegate; +import org.apache.fop.render.shading.FunctionPattern; + +public class PSFunction implements Function { + + private FunctionDelegate delegate; + + /** + * Creates a Postscript function dictionary + * @param theFunctionType The function type (0 = Sampled, 2 = Exponential + * Interpolation, 3 = Stitching) + * @param theDomain The function domain + * @param theRange Range used for clipping + * @param theFunctions An array of sub-functions such as determining the + * colour values used in a gradient. + * @param theBounds Bounds determines where each boundary exists for whatever + * the function is mean't. In a gradient case, it would be the point between + * colours. + * @param theEncode The function encoding + */ + public PSFunction(int theFunctionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode) { + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theFunctions, + theBounds, theEncode); + } + + /** + * Creates a Postscript function dictionary + * @param theFunctionType The function type (0 = Sampled, 2 = Exponential + * Interpolation, 3 = Stitching) + * @param theDomain The function domain + * @param theRange Range used for clipping + * @param theCZero In a gradient, this would be the first colour + * @param theCOne In a gradient, this would be the second colour + * @param theInterpolationExponentN Determines the number of values + * the function returns. + */ + public PSFunction(int theFunctionType, List theDomain, + List theRange, List theCZero, List theCOne, + double theInterpolationExponentN) { + delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theCZero, + theCOne, theInterpolationExponentN); + } + + /** + * Outputs the function to a byte array + */ + public byte[] toByteString() { + FunctionPattern pattern = new FunctionPattern(this); + try { + return pattern.toWriteableString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException ex) { + //This should have been made an enum type to avoid throwing exceptions. + return new byte[0]; + } + } + + public int getFunctionType() { + return delegate.getFunctionType(); + } + + public List getBounds() { + return delegate.getBounds(); + } + + public List getDomain() { + return delegate.getDomain(); + } + + public List getSize() { + return delegate.getSize(); + } + + public List getFilter() { + return delegate.getFilter(); + } + + public List getEncode() { + return delegate.getEncode(); + } + + public List getFunctions() { + return delegate.getFunctions(); + } + + public int getBitsPerSample() { + return delegate.getBitsPerSample(); + } + + public double getInterpolationExponentN() { + return delegate.getInterpolationExponentN(); + } + + public int getOrder() { + return delegate.getOrder(); + } + + public List getRange() { + return delegate.getRange(); + } + + public List getDecode() { + return delegate.getDecode(); + } + + public StringBuffer getDataStream() { + return delegate.getDataStream(); + } + + public List getCZero() { + return delegate.getCZero(); + } + + public List getCOne() { + return delegate.getCOne(); + } +} diff --git a/src/java/org/apache/fop/render/ps/svg/PSPattern.java b/src/java/org/apache/fop/render/ps/svg/PSPattern.java new file mode 100644 index 000000000..46f976672 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/svg/PSPattern.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps.svg; + +import java.util.List; + +import org.apache.fop.render.shading.Pattern; +import org.apache.fop.render.shading.Shading; + +public class PSPattern implements Pattern { + + /** + * Either one (1) for tiling, or two (2) for shading. + */ + protected int patternType = 2; // Default + + /** + * The Shading object comprising the Type 2 pattern + */ + protected PSShading shading = null; + + /** + * List of Integers represetning the Extended unique Identifier + */ + protected List xUID = null; + + /** + * TODO use PDFGState + * String representing the extended Graphics state. + * Probably will never be used like this. + */ + protected StringBuffer extGState = null; + + /** + * Creates a radial or axial shading pattern + * @param thePatternType The pattern type which will be 3 for radial and 2 for axial + * @param theShading The shading object to determine how the gradient + * is drawn + * @param theXUID The XUID + * @param theExtGState The exit state + */ + public PSPattern(int thePatternType, Shading theShading, List theXUID, + StringBuffer theExtGState) { + this.patternType = 2; // thePatternType; + assert theShading instanceof PSShading; + this.shading = (PSShading)theShading; + this.xUID = theXUID; + this.extGState = theExtGState; // always null + } + + /** + * Outputs the radial or axial pattern as a string dictionary to insert + * into a postscript document. + */ + public String toString() { + int vectorSize = 0; + int tempInt = 0; + StringBuffer p = new StringBuffer(64); + p.append("/Pattern setcolorspace\n"); + p.append("<< \n/Type /Pattern \n"); + + p.append("/PatternType " + this.patternType + " \n"); + + if (this.shading != null) { + p.append("/Shading " + this.shading.toString() + " \n"); + } + + if (this.xUID != null) { + vectorSize = this.xUID.size(); + p.append("/XUID [ "); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append((this.xUID.get(tempInt)) + " "); + } + p.append("] \n"); + } + + if (this.extGState != null) { + p.append("/ExtGState " + this.extGState + " \n"); + } + + p.append(">> \n"); + p.append("matrix makepattern setcolor\n"); + + return p.toString(); + } +} diff --git a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java new file mode 100644 index 000000000..3b6b5490c --- /dev/null +++ b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps.svg; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Paint; +import java.awt.geom.AffineTransform; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.batik.ext.awt.LinearGradientPaint; +import org.apache.batik.ext.awt.MultipleGradientPaint; +import org.apache.batik.ext.awt.RadialGradientPaint; + +import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; +import org.apache.xmlgraphics.ps.PSGenerator; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.GradientFactory; +import org.apache.fop.render.shading.GradientRegistrar; +import org.apache.fop.render.shading.PSGradientFactory; +import org.apache.fop.render.shading.Pattern; +import org.apache.fop.render.shading.Shading; + + +public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar { + + private static final Log LOG = LogFactory.getLog(PSSVGGraphics2D.class); + + /** + * Create a new Graphics2D that generates PostScript code. + * @param textAsShapes True if text should be rendered as graphics + * @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean) + */ + public PSSVGGraphics2D(boolean textAsShapes) { + super(textAsShapes); + } + + /** + * Create a new Graphics2D that generates PostScript code. + * @param textAsShapes True if text should be rendered as graphics + * @param gen PostScript generator to use for output + * @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean) + */ + public PSSVGGraphics2D(boolean textAsShapes, PSGenerator gen) { + super(textAsShapes, gen); + } + + /** + * Constructor for creating copies + * @param g parent PostScript Graphics2D + */ + public PSSVGGraphics2D(PSGraphics2D g) { + super(g); + } + + protected void applyPaint(Paint paint, boolean fill) { + super.applyPaint(paint, fill); + if (paint instanceof RadialGradientPaint) { + RadialGradientPaint rgp = (RadialGradientPaint)paint; + try { + handleRadialGradient(rgp, gen); + } catch (IOException ioe) { + handleIOException(ioe); + } + } else if (paint instanceof LinearGradientPaint) { + LinearGradientPaint lgp = (LinearGradientPaint)paint; + try { + handleLinearGradient(lgp, gen); + } catch (IOException ioe) { + handleIOException(ioe); + } + } + } + + private void handleLinearGradient(LinearGradientPaint lgp, PSGenerator gen) throws IOException { + MultipleGradientPaint.CycleMethodEnum cycle = lgp.getCycleMethod(); + if (cycle != MultipleGradientPaint.NO_CYCLE) { + return; + } + float[] fractions = lgp.getFractions(); + Color[] cols = lgp.getColors(); + + AffineTransform transform = new AffineTransform(getBaseTransform()); + transform.concatenate(getTransform()); + transform.concatenate(lgp.getTransform()); + + List theMatrix = new ArrayList(); + double [] mat = new double[6]; + transform.getMatrix(mat); + for (int idx = 0; idx < mat.length; idx++) { + theMatrix.add(Double.valueOf(mat[idx])); + } + + + List theCoords = new java.util.ArrayList(); + + + AffineTransform start = applyTransform(lgp.getTransform(), lgp.getStartPoint().getX(), lgp.getStartPoint().getY()); + AffineTransform end = applyTransform(lgp.getTransform(), lgp.getEndPoint().getX(), lgp.getEndPoint().getY()); + double startX = start.getTranslateX(); + double startY = start.getTranslateY(); + double endX = end.getTranslateX(); + double endY = end.getTranslateY(); + + double width = endX - startX; + double height = endY - startY; + + startX = startX + width * fractions[0]; + endX = endX - width * (1 - fractions[fractions.length - 1]); + startY = startY + (height * fractions[0]); + endY = endY - height * (1 - fractions[fractions.length - 1]); + + theCoords.add(startX); + theCoords.add(startY); + theCoords.add(endX); + theCoords.add(endY); + + + List someColors = new java.util.ArrayList(); + for (int count = 0; count < cols.length; count++) { + Color c1 = cols[count]; + if (c1.getAlpha() != 255) { + LOG.warn("Opacity is not currently supported for Postscript output"); + } + someColors.add(c1); + } + List theBounds = new java.util.ArrayList(); + for (int count = 1; count < fractions.length - 1; count++) { + float offset = fractions[count]; + theBounds.add(Double.valueOf(offset)); + } + PDFDeviceColorSpace colSpace; + colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); + + PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this); + PSPattern myPattern = gradientFactory.createGradient(false, colSpace, + someColors, theBounds, theCoords, theMatrix); + + gen.write(myPattern.toString()); + + } + + + + private void handleRadialGradient(RadialGradientPaint rgp, PSGenerator gen) throws IOException { + MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod(); + if (cycle != MultipleGradientPaint.NO_CYCLE) { + return; + } + + AffineTransform transform; + transform = new AffineTransform(getBaseTransform()); + transform.concatenate(getTransform()); + transform.concatenate(rgp.getTransform()); + + AffineTransform resultCentre = applyTransform(rgp.getTransform(), rgp.getCenterPoint().getX(), rgp.getCenterPoint().getY()); + AffineTransform resultFocus = applyTransform(rgp.getTransform(), rgp.getFocusPoint().getX(), rgp.getFocusPoint().getY()); + double scale = Math.sqrt(rgp.getTransform().getDeterminant()); + double radius = rgp.getRadius() * scale; + double centreX = resultCentre.getTranslateX(); + double centreY = resultCentre.getTranslateY(); + double focusX = resultFocus.getTranslateX(); + double focusY = resultFocus.getTranslateY(); + + List theMatrix = new java.util.ArrayList(); + double [] mat = new double[6]; + transform.getMatrix(mat); + for (int idx = 0; idx < mat.length; idx++) { + theMatrix.add(Double.valueOf(mat[idx])); + } + + List theCoords = new java.util.ArrayList(); + float[] fractions = rgp.getFractions(); + + theCoords.add(centreX); + theCoords.add(centreY); + theCoords.add(radius * rgp.getFractions()[0]); + theCoords.add(focusX); + theCoords.add(focusY); + theCoords.add(radius * fractions[fractions.length - 1]); + + Color[] cols = rgp.getColors(); + List someColors = new java.util.ArrayList(); + for (int count = 0; count < cols.length; count++) { + Color cc = cols[count]; + if (cc.getAlpha() != 255) { + /* This should never happen because radial gradients with opacity should now + * be rasterized in the PSImageHandlerSVG class. Please see the shouldRaster() + * method for more information. */ + LOG.warn("Opacity is not currently supported for Postscript output"); + } + + someColors.add(cc); + } + + List theBounds = new java.util.ArrayList(); + for (int count = 1; count < fractions.length - 1; count++) { + float offset = fractions[count]; + theBounds.add(Double.valueOf(offset)); + } + PDFDeviceColorSpace colSpace; + colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); + + PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this); + PSPattern myPattern = gradientFactory.createGradient(true, colSpace, + someColors, theBounds, theCoords, theMatrix); + + gen.write(myPattern.toString()); + } + + private AffineTransform applyTransform(AffineTransform base, double posX, double posY) { + AffineTransform result = AffineTransform.getTranslateInstance(posX, posY); + AffineTransform orig = base; + orig.concatenate(result); + return orig; + } + + protected AffineTransform getBaseTransform() { + AffineTransform at = new AffineTransform(this.getTransform()); + return at; + } + + /** + * Creates a new Graphics object that is + * a copy of this Graphics object. + * @return a new graphics context that is a copy of + * this graphics context. + */ + @Override + public Graphics create() { + preparePainting(); + return new PSSVGGraphics2D(this); + } + + /** + * Registers a function object against the output format document + * @param function The function object to register + * @return Returns either the function which has already been registered + * or the current new registered object. + */ + public Function registerFunction(Function function) { + //Objects aren't needed to be registered in Postscript + return function; + } + + /** + * Registers a shading object against the otuput format document + * @param shading The shading object to register + * @return Returs either the shading which has already been registered + * or the current new registered object + */ + public Shading registerShading(Shading shading) { + //Objects aren't needed to be registered in Postscript + return shading; + } + + /** + * Registers a pattern object against the output format document + * @param pattern The pattern object to register + * @return Returns either the pattern which has already been registered + * or the current new registered object + */ + public Pattern registerPattern(Pattern pattern) { + // TODO Auto-generated method stub + return pattern; + } +} diff --git a/src/java/org/apache/fop/render/ps/svg/PSShading.java b/src/java/org/apache/fop/render/ps/svg/PSShading.java new file mode 100644 index 000000000..7465fcadb --- /dev/null +++ b/src/java/org/apache/fop/render/ps/svg/PSShading.java @@ -0,0 +1,228 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps.svg; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.Shading; +import org.apache.fop.render.shading.ShadingPattern; + +public class PSShading implements Shading { + + /** + * Required: The Type of shading (1,2,3,4,5,6,7) + */ + protected int shadingType = 3; // Default + + /** + * A ColorSpace representing the colorspace. "DeviceRGB" is an example. + */ + protected PDFDeviceColorSpace colorSpace = null; + + /** + * The background color. Since shading is opaque, + * this is very rarely used. + */ + protected List background = null; + + /** + * Optional: A List specifying the clipping rectangle + */ + protected List bBox = null; + + /** + * Optional: A flag whether or not to filter the shading function + * to prevent aliasing artifacts. Default is false. + */ + protected boolean antiAlias = false; + + /** + * Optional for Type 1: Array of four numbers, xmin, xmax, ymin, ymax. + * Default is [0 1 0 1] + * Optional for Type 2: An array of two numbers between which the blend + * varies between start and end points. Default is 0, 1. + * Optional for Type 3: An array of two numbers between which the blend + * varies between start and end points. Default is 0, 1. + */ + protected List domain = null; + + /** + * Required for Type 1, 2, and 3: + * The object of the color mapping function (usually type 2 or 3). + * Optional for Type 4,5,6, and 7: When it's nearly the same thing. + */ + protected PSFunction function = null; + + /** + * Required for Type 2: An Array of four numbers specifying + * the starting and ending coordinate pairs + * Required for Type 3: An Array of six numbers [x0,y0,r0,x1,y1,r1] + * specifying the centers and radii of + * the starting and ending circles. + */ + protected List coords = null; + + /** + * Required for Type 2+3: An Array of two boolean values specifying + * whether to extend the start and end colors past the start + * and end points, respectively. + * Default is false, false. + */ + protected List extend = null; + + /** + * Constructor for Type 2 and 3 + * + * @param theShadingType 2 or 3 for axial or radial shading + * @param theColorSpace "DeviceRGB" or similar. + * @param theBackground theBackground An array of color components appropriate to the + * colorspace key specifying a single color value. + * This key is used by the f operator buy ignored by the sh operator. + * @param theBBox List of double's representing a rectangle + * in the coordinate space that is current at the + * time of shading is imaged. Temporary clipping + * boundary. + * @param theAntiAlias Default is false + * @param theCoords List of four (type 2) or 6 (type 3) Double + * @param theDomain List of Doubles specifying the domain + * @param theFunction the Stitching (PDFfunction type 3) function, + * even if it's stitching a single function + * @param theExtend List of Booleans of whether to extend the start + * and end colors past the start and end points + * The default is [false, false] + */ + public PSShading(int theShadingType, PDFDeviceColorSpace theColorSpace, + List theBackground, List theBBox, + boolean theAntiAlias, List theCoords, + List theDomain, Function theFunction, + List theExtend) { + this.shadingType = theShadingType; // 2 or 3 + this.colorSpace = theColorSpace; + this.background = theBackground; + this.bBox = theBBox; + this.antiAlias = theAntiAlias; + + this.coords = theCoords; + this.domain = theDomain; + assert theFunction instanceof PSFunction; + this.function = (PSFunction)theFunction; + this.extend = theExtend; + } + + /** + * represent as PS. Whatever the shadingType is, the correct + * representation spits out. The sets of required and optional + * attributes are different for each type, but if a required + * attribute's object was constructed as null, then no error + * is raised. Instead, the malformed PS that was requested + * by the construction is dutifully output. + * This policy should be reviewed. + * + * @return the PDF string. + */ + public String toString() { + ShadingPattern pattern = new ShadingPattern(this); + return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias); + } + + /** + * A method to write a type 2 or 3 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType2or3(StringBuffer p) { + if (this.coords != null) { + p.append("\t/Coords [ "); + for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) { + p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex)) + + " "); + } + p.append("] \n"); + } + + // DOMAIN + if (this.domain != null) { + p.append("\t/Domain [ "); + for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) { + p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex)) + + " "); + } + p.append("] \n"); + } else { + p.append("\t/Domain [ 0 1 ] \n"); + } + + if (this.extend != null) { + p.append("\t/Extend [ "); + for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) { + p.append((this.extend.get(extendIndex)) + " "); + } + + p.append("] \n"); + } else { + p.append("\t/Extend [ true true ] \n"); + } + + + if (this.function != null) { + p.append("\t/Function "); + try { + p.append(new String(this.function.toByteString(), "UTF-8") + " \n"); + } catch (UnsupportedEncodingException ex) { + //This should have been made an enum type to avoid throwing exceptions. + } + } + return p; + } + + /** + * A method to write a type 1 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType1(StringBuffer p) { + // TODO Auto-generated method stub + return null; + } + + /** + * A method to write a type 4, 6 or 7 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType4or6or7(StringBuffer p) { + // TODO Auto-generated method stub + return null; + } + + /** + * A method to write a type 5 shading object + * @param p The StringBuffer to write the shading object + * @return Returns the StringBuffer to which the shading object was written + */ + public StringBuffer handleShadingType5(StringBuffer p) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/java/org/apache/fop/render/shading/Function.java b/src/java/org/apache/fop/render/shading/Function.java new file mode 100644 index 000000000..5bd44087e --- /dev/null +++ b/src/java/org/apache/fop/render/shading/Function.java @@ -0,0 +1,39 @@ +/* + * 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.render.shading; + +import java.util.List; + +public interface Function { + int getFunctionType(); + List getBounds(); + List getDomain(); + List getSize(); + List getFilter(); + List getEncode(); + List getFunctions(); + int getBitsPerSample(); + double getInterpolationExponentN(); + int getOrder(); + List getRange(); + List getDecode(); + StringBuffer getDataStream(); + List getCZero(); + List getCOne(); + byte[] toByteString(); +} diff --git a/src/java/org/apache/fop/render/shading/FunctionDelegate.java b/src/java/org/apache/fop/render/shading/FunctionDelegate.java new file mode 100644 index 000000000..eea24e0db --- /dev/null +++ b/src/java/org/apache/fop/render/shading/FunctionDelegate.java @@ -0,0 +1,451 @@ +/* + * 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.render.shading; + +import java.util.List; + +public class FunctionDelegate implements Function { + + private Function parentFunction; + + /** + * Required: The Type of function (0,2,3,4) default is 0. + */ + protected int functionType = 0; // Default + + /** + * Required: 2 * m Array of Double numbers which are possible inputs to the function + */ + protected List domain = null; + + /** + * Required: 2 * n Array of Double numbers which are possible outputs to the function + */ + protected List range = null; + + /* ********************TYPE 0***************************** */ + // FunctionType 0 specific function guts + + /** + * Required: Array containing the Integer size of the Domain and Range, respectively. + * Note: This is really more like two seperate integers, sizeDomain, and sizeRange, + * but since they're expressed as an array in PDF, my implementation reflects that. + */ + protected List size = null; + + /** + * Required for Type 0: Number of Bits used to represent each sample value. + * Limited to 1,2,4,8,12,16,24, or 32 + */ + protected int bitsPerSample = 1; + + /** + * Optional for Type 0: order of interpolation between samples. + * Limited to linear (1) or cubic (3). Default is 1 + */ + protected int order = 1; + + /** + * Optional for Type 0: A 2 * m array of Doubles which provides a + * linear mapping of input values to the domain. + * + * Required for Type 3: A 2 * k array of Doubles that, taken + * in pairs, map each subset of the domain defined by Domain + * and the Bounds array to the domain of the corresponding function. + * Should be two values per function, usually (0,1), + * as in [0 1 0 1] for 2 functions. + */ + protected List encode = null; + + /** + * Optional for Type 0: A 2 * n array of Doubles which provides + * a linear mapping of sample values to the range. Defaults to Range. + */ + protected List decode = null; + + /** + * Optional For Type 0: A stream of sample values + */ + + /** + * Required For Type 4: Postscript Calculator function + * composed of arithmetic, boolean, and stack operators + boolean constants + */ + protected StringBuffer functionDataStream = null; + + /** + * Required (possibly) For Type 0: A vector of Strings for the + * various filters to be used to decode the stream. + * These are how the string is compressed. Flate, LZW, etc. + */ + protected List filter = null; + /* *************************TYPE 2************************** */ + + /** + * Required For Type 2: An Array of n Doubles defining + * the function result when x=0. Default is [0]. + */ + protected List cZero = null; + + /** + * Required For Type 2: An Array of n Doubles defining + * the function result when x=1. Default is [1]. + */ + protected List cOne = null; + + /** + * Required for Type 2: The interpolation exponent. + * Each value x will return n results. + * Must be greater than 0. + */ + protected double interpolationExponentN = 1; + + /* *************************TYPE 3************************** */ + + /** + * Required for Type 3: An vector of PDFFunctions which + * form an array of k single input functions making up + * the stitching function. + */ + protected List functions = null; + + /** + * Optional for Type 3: An array of (k-1) Doubles that, + * in combination with Domain, define the intervals to which + * each function from the Functions array apply. Bounds + * elements must be in order of increasing magnitude, + * and each value must be within the value of Domain. + * k is the number of functions. + * If you pass null, it will output (1/k) in an array of k-1 elements. + * This makes each function responsible for an equal amount of the stitching function. + * It makes the gradient even. + */ + protected List bounds = null; + + /** + * create an complete Function object of Type 0, A Sampled function. + * + * Use null for an optional object parameter if you choose not to use it. + * For optional int parameters, pass the default. + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List objects of Double objects. + * This is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theSize A List object of Integer objects. + * This is the number of samples in each input dimension. + * I can't imagine there being more or less than two input dimensions, + * so maybe this should be an array of length 2. + * + * See page 265 of the PDF 1.3 Spec. + * @param theBitsPerSample An int specifying the number of bits + used to represent each sample value. + * Limited to 1,2,4,8,12,16,24 or 32. + * See page 265 of the 1.3 PDF Spec. + * @param theOrder The order of interpolation between samples. Default is 1 (one). Limited + * to 1 (one) or 3, which means linear or cubic-spline interpolation. + * + * This attribute is optional. + * + * See page 265 in the PDF 1.3 spec. + * @param theEncode List objects of Double objects. + * This is the linear mapping of input values intop the domain + * of the function's sample table. Default is hard to represent in + * ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. + * This attribute is optional. + * + * See page 265 in the PDF 1.3 spec. + * @param theDecode List objects of Double objects. + * This is a linear mapping of sample values into the range. + * The default is just the range. + * + * This attribute is optional. + * Read about it on page 265 of the PDF 1.3 spec. + * @param theFunctionDataStream The sample values that specify + * the function are provided in a stream. + * + * This is optional, but is almost always used. + * + * Page 265 of the PDF 1.3 spec has more. + * @param theFilter This is a vector of String objects which are the various filters that + * have are to be applied to the stream to make sense of it. Order matters, + * so watch out. + * + * This is not documented in the Function section of the PDF 1.3 spec, + * it was deduced from samples that this is sometimes used, even if we may never + * use it in FOP. It is added for completeness sake. + * @param theFunctionType This is the type of function (0,2,3, or 4). + * It should be 0 as this is the constructor for sampled functions. + */ + public FunctionDelegate(Function parentFunction, int theFunctionType, List theDomain, + List theRange, List theSize, int theBitsPerSample, + int theOrder, List theEncode, List theDecode, + StringBuffer theFunctionDataStream, List theFilter) { + this.parentFunction = parentFunction; + this.functionType = 0; // dang well better be 0; + this.size = theSize; + this.bitsPerSample = theBitsPerSample; + this.order = theOrder; // int + this.encode = theEncode; // vector of int + this.decode = theDecode; // vector of int + this.functionDataStream = theFunctionDataStream; + this.filter = theFilter; // vector of Strings + + // the domain and range are actually two dimensional arrays. + // so if there's not an even number of items, bad stuff + // happens. + this.domain = theDomain; + this.range = theRange; + } + + /** + * create an complete Function object of Type 2, an Exponential Interpolation function. + * + * Use null for an optional object parameter if you choose not to use it. + * For optional int parameters, pass the default. + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List of Doubles that is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theCZero This is a vector of Double objects which defines the function result + * when x=0. + * + * This attribute is optional. + * It's described on page 268 of the PDF 1.3 spec. + * @param theCOne This is a vector of Double objects which defines the function result + * when x=1. + * + * This attribute is optional. + * It's described on page 268 of the PDF 1.3 spec. + * @param theInterpolationExponentN This is the inerpolation exponent. + * + * This attribute is required. + * PDF Spec page 268 + * @param theFunctionType The type of the function, which should be 2. + */ + public FunctionDelegate(Function parentFunction, int theFunctionType, List theDomain, + List theRange, List theCZero, List theCOne, + double theInterpolationExponentN) { + this.parentFunction = parentFunction; + this.functionType = 2; // dang well better be 2; + + this.cZero = theCZero; + this.cOne = theCOne; + this.interpolationExponentN = theInterpolationExponentN; + + this.domain = theDomain; + this.range = theRange; + + } + + /** + * create an complete Function object of Type 3, a Stitching function. + * + * Use null for an optional object parameter if you choose not to use it. + * For optional int parameters, pass the default. + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List objects of Double objects. + * This is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theFunctions A List of the PDFFunction objects that the stitching function stitches. + * + * This attributed is required. + * It is described on page 269 of the PDF spec. + * @param theBounds This is a vector of Doubles representing the numbers that, + * in conjunction with Domain define the intervals to which each function from + * the 'functions' object applies. It must be in order of increasing magnitude, + * and each must be within Domain. + * + * It basically sets how much of the gradient each function handles. + * + * This attributed is required. + * It's described on page 269 of the PDF 1.3 spec. + * @param theEncode List objects of Double objects. + * This is the linear mapping of input values intop the domain + * of the function's sample table. Default is hard to represent in + * ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. + * This attribute is required. + * + * See page 270 in the PDF 1.3 spec. + * @param theFunctionType This is the function type. It should be 3, + * for a stitching function. + */ + public FunctionDelegate(Function parentFunction, int theFunctionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode) { + this.parentFunction = parentFunction; + this.functionType = 3; // dang well better be 3; + + this.functions = theFunctions; + this.bounds = theBounds; + this.encode = theEncode; + this.domain = theDomain; + this.range = theRange; + + } + + /** + * create an complete Function object of Type 4, a postscript calculator function. + * + * Use null for an optional object parameter if you choose not to use it. + * For optional int parameters, pass the default. + * + * @param theDomain List object of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List object of Double objects. + * This is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theFunctionDataStream This is a stream of arithmetic, + * boolean, and stack operators and boolean constants. + * I end up enclosing it in the '{' and '}' braces for you, so don't do it + * yourself. + * + * This attribute is required. + * It's described on page 269 of the PDF 1.3 spec. + * @param theFunctionType The type of function which should be 4, as this is + * a Postscript calculator function + */ + public FunctionDelegate(Function parentFunction, int theFunctionType, List theDomain, + List theRange, StringBuffer theFunctionDataStream) { + this.parentFunction = parentFunction; + this.functionType = 4; // dang well better be 4; + this.functionDataStream = theFunctionDataStream; + + this.domain = theDomain; + + this.range = theRange; + + } + + /** + * Gets the function type + */ + public int getFunctionType() { + return functionType; + } + + /** + * Gets the function bounds + */ + public List getBounds() { + return bounds; + } + + /** + * The function domain + */ + public List getDomain() { + return domain; + } + + /** + * The function size + */ + public List getSize() { + return size; + } + + /** + * Gets the function encoding + */ + public List getEncode() { + return encode; + } + + /** + * Gets the sub-functions + */ + public List getFunctions() { + return functions; + } + + /** + * Gets the function filter + */ + public List getFilter() { + return filter; + } + + /** + * Gets the bits per sample of the function + */ + public int getBitsPerSample() { + return bitsPerSample; + } + + /** + * Gets the interpolation exponent of the function + */ + public double getInterpolationExponentN() { + return interpolationExponentN; + } + + /** + * Gets the function order + */ + public int getOrder() { + return order; + } + + /** + * Gets the function range + */ + public List getRange() { + return range; + } + + /** + * Gets the function decoding + */ + public List getDecode() { + return decode; + } + + /** + * Gets the function data stream + */ + public StringBuffer getDataStream() { + return functionDataStream; + } + + /** + * Gets the function C0 value (color for gradient) + */ + public List getCZero() { + return cZero; + } + + /** + * Gets the function C1 value (color for gradient) + */ + public List getCOne() { + return cOne; + } + + public byte[] toByteString() { + return parentFunction.toByteString(); + } +} diff --git a/src/java/org/apache/fop/render/shading/FunctionPattern.java b/src/java/org/apache/fop/render/shading/FunctionPattern.java new file mode 100644 index 000000000..044053a8b --- /dev/null +++ b/src/java/org/apache/fop/render/shading/FunctionPattern.java @@ -0,0 +1,363 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.shading; + +import java.io.UnsupportedEncodingException; + +import org.apache.fop.pdf.PDFFunction; +import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.render.ps.svg.PSFunction; + +/** + * A class for writing function objects for different output formats + */ +public class FunctionPattern { + + private Function function; + + /** + * Constructor + * @param function The function from which to write the output + */ + public FunctionPattern(Function function) { + this.function = function; + } + + /** + * Outputs the function to a byte array + */ + public String toWriteableString() { + int vectorSize = 0; + int numberOfFunctions = 0; + int tempInt = 0; + StringBuffer p = new StringBuffer(256); + p.append("<< \n/FunctionType " + function.getFunctionType() + " \n"); + + // FunctionType 0 + if (this.function.getFunctionType() == 0) { + if (function.getDomain() != null) { + // DOMAIN + p.append("/Domain [ "); + vectorSize = function.getDomain().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) + + " "); + } + + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + // SIZE + if (function.getSize() != null) { + p.append("/Size [ "); + vectorSize = function.getSize().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getSize().get(tempInt)) + + " "); + } + p.append("] \n"); + } + // ENCODE + if (function.getEncode() != null) { + p.append("/Encode [ "); + vectorSize = function.getEncode().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt)) + + " "); + } + p.append("] \n"); + } else { + p.append("/Encode [ "); + vectorSize = function.getFunctions().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append("0 1 "); + } + p.append("] \n"); + + } + + // BITSPERSAMPLE + p.append("/BitsPerSample " + function.getBitsPerSample()); + + // ORDER (optional) + if (function.getOrder() == 1 || function.getOrder() == 3) { + p.append(" \n/Order " + function.getOrder() + " \n"); + } + + // RANGE + if (function.getRange() != null) { + p.append("/Range [ "); + vectorSize = function.getRange().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) + + " "); + } + + p.append("] \n"); + } + + // DECODE + if (function.getDecode() != null) { + p.append("/Decode [ "); + vectorSize = function.getDecode().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getDecode().get(tempInt)) + + " "); + } + + p.append("] \n"); + } + + // LENGTH + if (function.getDataStream() != null) { + p.append("/Length " + (function.getDataStream().length() + 1) + + " \n"); + } + + // FILTER? + if (function.getFilter() != null) { // if there's a filter + vectorSize = function.getFilter().size(); + p.append("/Filter "); + if (vectorSize == 1) { + p.append("/" + (function.getFilter().get(0)) + + " \n"); + } else { + p.append("[ "); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append("/" + (function.getFilter().get(0)) + + " "); + } + p.append("] \n"); + } + } + p.append(">>"); + + // stream representing the function + if (function.getDataStream() != null) { + p.append("\nstream\n" + function.getDataStream() + + "\nendstream"); + } + + // end of if FunctionType 0 + + } else if (function.getFunctionType() == 2) { + // DOMAIN + if (function.getDomain() != null) { + p.append("/Domain [ "); + vectorSize = function.getDomain().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) + + " "); + } + + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + + // RANGE + if (function.getRange() != null) { + p.append("/Range [ "); + vectorSize = function.getRange().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) + + " "); + } + + p.append("] \n"); + } + + // FunctionType, C0, C1, N are required in PDF + + // C0 + if (function.getCZero() != null) { + p.append("/C0 [ "); + vectorSize = function.getCZero().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getCZero().get(tempInt)) + + " "); + } + p.append("] \n"); + } + + // C1 + if (function.getCOne() != null) { + p.append("/C1 [ "); + vectorSize = function.getCOne().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getCOne().get(tempInt)) + + " "); + } + p.append("] \n"); + } + + // N: The interpolation Exponent + p.append("/N " + + PDFNumber.doubleOut(Double.valueOf(function.getInterpolationExponentN())) + + " \n"); + + p.append(">>"); + + } else if (function.getFunctionType() + == 3) { // fix this up when my eyes uncross + // DOMAIN + if (function.getDomain() != null) { + p.append("/Domain [ "); + vectorSize = function.getDomain().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) + + " "); + } + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + // RANGE + if (function.getRange() != null) { + p.append("/Range [ "); + vectorSize = function.getRange().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) + + " "); + } + + p.append("] \n"); + } + + // FUNCTIONS + if (function.getFunctions() != null) { + p.append("/Functions [ "); + numberOfFunctions = function.getFunctions().size(); + for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) { + try { + if (function instanceof PSFunction) { + p.append(new String(function.getFunctions().get(tempInt).toByteString(), "UTF-8") + + " "); + } else { + p.append(((PDFFunction)function.getFunctions().get(tempInt)).referencePDF() + + " "); + } + } catch (UnsupportedEncodingException ex) { + //This should have been made an enum type to avoid throwing exceptions. + } + } + p.append("] \n"); + } + + + // ENCODE + if (function.getEncode() != null) { + p.append("/Encode [ "); + vectorSize = function.getEncode().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt)) + + " "); + } + + p.append("] \n"); + } else { + p.append("/Encode [ "); + vectorSize = function.getFunctions().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append("0 1 "); + } + p.append("] \n"); + + } + + + // BOUNDS, required, but can be empty + p.append("/Bounds [ "); + if (function.getBounds() != null) { + + vectorSize = function.getBounds().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getBounds().get(tempInt)) + + " "); + } + + } else { + if (function.getFunctions() != null) { + // if there are n functions, + // there must be n-1 bounds. + // so let each function handle an equal portion + // of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ] + + String functionsFraction = PDFNumber.doubleOut(Double.valueOf(1.0 + / (numberOfFunctions))); + + for (tempInt = 0; tempInt + 1 < numberOfFunctions; + tempInt++) { + + p.append(functionsFraction + " "); + } + } + + } + p.append("]\n>>"); + } else if (function.getFunctionType() + == 4) { // fix this up when my eyes uncross + // DOMAIN + if (function.getDomain() != null) { + p.append("/Domain [ "); + vectorSize = function.getDomain().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt)) + + " "); + } + + p.append("] \n"); + } else { + p.append("/Domain [ 0 1 ] \n"); + } + + // RANGE + if (function.getRange() != null) { + p.append("/Range [ "); + vectorSize = function.getRange().size(); + for (tempInt = 0; tempInt < vectorSize; tempInt++) { + p.append(PDFNumber.doubleOut(function.getRange().get(tempInt)) + + " "); + } + + p.append("] \n"); + } + + // LENGTH + if (function.getDataStream() != null) { + p.append("/Length " + (function.getDataStream().length() + 1) + + " \n"); + } + + p.append(">>"); + + // stream representing the function + if (function.getDataStream() != null) { + p.append("\nstream\n{ " + function.getDataStream() + + " }\nendstream"); + } + } + return p.toString(); + } +} diff --git a/src/java/org/apache/fop/render/shading/GradientFactory.java b/src/java/org/apache/fop/render/shading/GradientFactory.java new file mode 100644 index 000000000..87ac11c83 --- /dev/null +++ b/src/java/org/apache/fop/render/shading/GradientFactory.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.shading; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +import org.apache.xmlgraphics.java2d.color.ColorUtil; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.render.ps.svg.PSSVGGraphics2D; + +public abstract class GradientFactory { + + static GradientRegistrar registrar; + + /** + * Constructor + * @param registrar The object used to register new embedded objects in the + * output format. + */ + public static GradientFactory newInstance(GradientRegistrar theRegistrar) { + registrar = theRegistrar; + if (registrar instanceof PSSVGGraphics2D) { + return new PSGradientFactory(); + } else { + return new PDFGradientFactory(); + } + } + + /** + * Creates a new gradient + * @param radial Determines whether the gradient is radial + * @param theColorspace The colorspace used in PDF and Postscript + * @param theColors The colors to be used in the gradient + * @param theBounds The bounds of each color + * @param theCoords The co-ordinates of the gradient + * @param theMatrix The matrix for any transformations + * @return Returns the Pattern object of the gradient + */ + public abstract Pattern createGradient(boolean radial, + PDFDeviceColorSpace theColorspace, List theColors, List theBounds, + List theCoords, List theMatrix); + + protected Pattern makeGradient(boolean radial, PDFDeviceColorSpace theColorspace, + List theColors, List theBounds, + List theCoords, List theMatrix) { + Shading myShad; + Function myfunky; + Function myfunc; + List theCzero; + List theCone; + double interpolation = 1.000; + List theFunctions = new ArrayList(); + + int currentPosition; + int lastPosition = theColors.size() - 1; + + + // if 5 elements, the penultimate element is 3. + // do not go beyond that, because you always need + // to have a next color when creating the function. + + for (currentPosition = 0; currentPosition < lastPosition; + currentPosition++) { // for every consecutive color pair + Color currentColor = theColors.get(currentPosition); + Color nextColor = theColors.get(currentPosition + 1); + + // colorspace must be consistent, so we simply convert to sRGB where necessary + if (!currentColor.getColorSpace().isCS_sRGB()) { + //Convert to sRGB + currentColor = ColorUtil.toSRGBColor(currentColor); + theColors.set(currentPosition, currentColor); + } + if (!nextColor.getColorSpace().isCS_sRGB()) { + //Convert to sRGB + nextColor = ColorUtil.toSRGBColor(nextColor); + theColors.set(currentPosition + 1, nextColor); + } + + theCzero = toColorVector(currentColor); + theCone = toColorVector(nextColor); + + myfunc = makeFunction(2, null, null, theCzero, theCone, + interpolation); + + theFunctions.add(myfunc); + + } // end of for every consecutive color pair + + myfunky = makeFunction(3, null, null, theFunctions, theBounds, + null); + + if (radial) { + if (theCoords.size() == 6) { + // make Shading of Type 2 or 3 + myShad = makeShading(3, theColorspace, null, null, false, theCoords, + null, myfunky, null); + } else { // if the center x, center y, and radius specifiy + // the gradient, then assume the same center x, center y, + // and radius of zero for the other necessary component + List newCoords = new ArrayList(); + newCoords.add(theCoords.get(0)); + newCoords.add(theCoords.get(1)); + newCoords.add(theCoords.get(2)); + newCoords.add(theCoords.get(0)); + newCoords.add(theCoords.get(1)); + newCoords.add(Double.valueOf(0.0)); + + myShad = makeShading(3, theColorspace, null, null, false, newCoords, + null, myfunky, null); + } + } else { + myShad = makeShading(2, theColorspace, null, null, false, theCoords, + null, myfunky, null); + } + return makePattern(2, myShad, null, null, theMatrix); + } + + public abstract Function makeFunction(int functionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode); + + public abstract Function makeFunction(int functionType, List theDomain, + List theRange, List theCZero, List theCOne, + double theInterpolationExponentN); + + public abstract Shading makeShading(int theShadingType, + PDFDeviceColorSpace theColorSpace, List theBackground, List theBBox, + boolean theAntiAlias, List theCoords, List theDomain, + Function theFunction, List theExtend); + + public abstract Pattern makePattern(int thePatternType, Shading theShading, List theXUID, + StringBuffer theExtGState, List theMatrix); + + private List toColorVector(Color nextColor) { + List vector = new java.util.ArrayList(); + float[] comps = nextColor.getColorComponents(null); + for (int i = 0, c = comps.length; i < c; i++) { + vector.add(Double.valueOf(comps[i])); + } + return vector; + } +} diff --git a/src/java/org/apache/fop/render/shading/GradientRegistrar.java b/src/java/org/apache/fop/render/shading/GradientRegistrar.java new file mode 100644 index 000000000..617fcd4fb --- /dev/null +++ b/src/java/org/apache/fop/render/shading/GradientRegistrar.java @@ -0,0 +1,45 @@ +/* + * 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.render.shading; + +public interface GradientRegistrar { + + /** + * Registers a function object against the output format document + * @param function The function object to register + * @return Returns either the function which has already been registered + * or the current new registered object. + */ + Function registerFunction(Function function); + + /** + * Registers a shading object against the output format document + * @param shading The shading object to register + * @return Returns either the shading which has already been registered + * or the current new registered object + */ + Shading registerShading(Shading shading); + + /** + * Registers a pattern object against the output format document + * @param pattern The pattern object to register + * @return Returns either the pattern which has already been registered + * or the current new registered object + */ + Pattern registerPattern(Pattern pattern); +} diff --git a/src/java/org/apache/fop/render/shading/PDFGradientFactory.java b/src/java/org/apache/fop/render/shading/PDFGradientFactory.java new file mode 100644 index 000000000..3b3dcab75 --- /dev/null +++ b/src/java/org/apache/fop/render/shading/PDFGradientFactory.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.fop.render.shading; + +import java.awt.Color; +import java.util.List; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.pdf.PDFFunction; +import org.apache.fop.pdf.PDFPattern; +import org.apache.fop.pdf.PDFShading; + +public class PDFGradientFactory extends GradientFactory { + + @Override + public PDFPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace, List theColors, + List theBounds, List theCoords, List theMatrix) { + return (PDFPattern)makeGradient(radial, theColorspace, theColors, theBounds, + theCoords, theMatrix); + } + + @Override + public Function makeFunction(int functionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode) { + Function newFunction = new PDFFunction(functionType, theDomain, theRange, theFunctions, + theBounds, theEncode); + newFunction = registrar.registerFunction(newFunction); + return newFunction; + } + + public Function makeFunction(int functionType, List theDomain, + List theRange, List theCZero, List theCOne, + double theInterpolationExponentN) { + Function newFunction = new PDFFunction(functionType, theDomain, theRange, theCZero, + theCOne, theInterpolationExponentN); + newFunction = registrar.registerFunction(newFunction); + return newFunction; + } + + @Override + public Shading makeShading(int theShadingType, + PDFDeviceColorSpace theColorSpace, List theBackground, List theBBox, + boolean theAntiAlias, List theCoords, List theDomain, + Function theFunction, List theExtend) { + Shading newShading = new PDFShading(theShadingType, theColorSpace, theBackground, + theBBox, theAntiAlias, theCoords, theDomain, theFunction, theExtend); + newShading = registrar.registerShading(newShading); + return newShading; + } + + @Override + public Pattern makePattern(int thePatternType, Shading theShading, List theXUID, + StringBuffer theExtGState, List theMatrix) { + Pattern newPattern = new PDFPattern(thePatternType, theShading, theXUID, theExtGState, + theMatrix); + newPattern = registrar.registerPattern(newPattern); + return newPattern; + } + +} diff --git a/src/java/org/apache/fop/render/shading/PSGradientFactory.java b/src/java/org/apache/fop/render/shading/PSGradientFactory.java new file mode 100644 index 000000000..cd47de93a --- /dev/null +++ b/src/java/org/apache/fop/render/shading/PSGradientFactory.java @@ -0,0 +1,70 @@ +/* + * 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.render.shading; + +import java.awt.Color; +import java.util.List; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.render.ps.svg.PSFunction; +import org.apache.fop.render.ps.svg.PSPattern; +import org.apache.fop.render.ps.svg.PSShading; + +public class PSGradientFactory extends GradientFactory { + + @Override + public PSPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace, + List theColors, List theBounds, List theCoords, + List theMatrix) { + return (PSPattern)makeGradient(radial, theColorspace, theColors, theBounds, + theCoords, theMatrix); + } + + public Function makeFunction(int functionType, List theDomain, + List theRange, List theFunctions, + List theBounds, List theEncode) { + Function newFunction = new PSFunction(functionType, theDomain, theRange, theFunctions, + theBounds, theEncode); + return newFunction; + } + + @Override + public Function makeFunction(int functionType, List theDomain, + List theRange, List theCZero, List theCOne, + double theInterpolationExponentN) { + Function newFunction = new PSFunction(functionType, theDomain, theRange, theCZero, + theCOne, theInterpolationExponentN); + return newFunction; + } + + @Override + public Shading makeShading(int theShadingType, + PDFDeviceColorSpace theColorSpace, List theBackground, List theBBox, + boolean theAntiAlias, List theCoords, List theDomain, + Function theFunction, List theExtend) { + Shading newShading = new PSShading(theShadingType, theColorSpace, theBackground, theBBox, + theAntiAlias, theCoords, theDomain, theFunction, theExtend); + return newShading; + } + + @Override + public Pattern makePattern(int thePatternType, Shading theShading, List theXUID, + StringBuffer theExtGState, List theMatrix) { + return new PSPattern(thePatternType, theShading, theXUID, theExtGState); + } +} diff --git a/src/java/org/apache/fop/render/shading/Pattern.java b/src/java/org/apache/fop/render/shading/Pattern.java new file mode 100644 index 000000000..b66926e53 --- /dev/null +++ b/src/java/org/apache/fop/render/shading/Pattern.java @@ -0,0 +1,22 @@ +/* + * 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.render.shading; + +public interface Pattern { + +} diff --git a/src/java/org/apache/fop/render/shading/Shading.java b/src/java/org/apache/fop/render/shading/Shading.java new file mode 100644 index 000000000..385cb112b --- /dev/null +++ b/src/java/org/apache/fop/render/shading/Shading.java @@ -0,0 +1,26 @@ +/* + * 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.render.shading; + + +public interface Shading { + StringBuffer handleShadingType1(StringBuffer p); + StringBuffer handleShadingType2or3(StringBuffer p); + StringBuffer handleShadingType4or6or7(StringBuffer p); + StringBuffer handleShadingType5(StringBuffer p); +} diff --git a/src/java/org/apache/fop/render/shading/ShadingPattern.java b/src/java/org/apache/fop/render/shading/ShadingPattern.java new file mode 100644 index 000000000..6dac65f55 --- /dev/null +++ b/src/java/org/apache/fop/render/shading/ShadingPattern.java @@ -0,0 +1,105 @@ +/* + * 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.render.shading; + +import java.util.List; + +import org.apache.fop.pdf.PDFDeviceColorSpace; +import org.apache.fop.pdf.PDFNumber; + +/** + * A class for writing shading objects for different output formats + */ +public class ShadingPattern { + + private Shading shading; + + /** + * Constructor + * @param shading The shading object from which to write the output + */ + public ShadingPattern(Shading shading) { + this.shading = shading; + } + + /** + * Outputs the given shading object to a String + * @param colorSpace The Colospace (PDF and Postscript) + * @param shadingType The shading type + * @param background The background + * @param bBox The bounding box + * @param antiAlias Anti-aliasing + * @return Returns the output string + */ + public String toString(PDFDeviceColorSpace colorSpace, int shadingType, List background, + List bBox, boolean antiAlias) { + StringBuffer p = new StringBuffer(128); + p.append("<<\n/ShadingType " + shadingType + " \n"); + if (colorSpace != null) { + p.append("/ColorSpace /" + + colorSpace.getName() + " \n"); + } + + if (background != null) { + p.append("/Background [ "); + for (int bgIndex = 0; bgIndex < background.size(); bgIndex++) { + p.append(PDFNumber.doubleOut((Double)background.get(bgIndex)) + + " "); + } + p.append("] \n"); + } + + if (bBox + != null) { // I've never seen an example, so I guess this is right. + p.append("/BBox [ "); + for (int bboxIndex = 0; bboxIndex < bBox.size(); bboxIndex++) { + p.append(PDFNumber.doubleOut((Double)bBox.get(bboxIndex)) + + " "); + } + p.append("] \n"); + } + + if (antiAlias) { + p.append("/AntiAlias " + antiAlias + " \n"); + } + + // Here's where we differentiate based on what type it is. + switch (shadingType) { + //Function based shading + case 1: p = shading.handleShadingType1(p); break; + //Axial shading + case 2: + //Radial shading + case 3: p = shading.handleShadingType2or3(p); break; + //Free-form Gouraud-shaded triangle meshes + case 4: + //Coons patch meshes + case 6: + //Tensor product patch meshes + case 7: p = shading.handleShadingType4or6or7(p); break; + //Lattice Free form gouraud-shaded triangle mesh + case 5: p = shading.handleShadingType5(p); break; + default: //Shading pattern outside expecting values + break; + } + + p.append(">>"); + + return (p.toString()); + } +} diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index efa71a225..1fcf9f870 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -80,6 +80,7 @@ import org.apache.fop.pdf.PDFColorHandler; import org.apache.fop.pdf.PDFConformanceException; import org.apache.fop.pdf.PDFDeviceColorSpace; import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFFunction; import org.apache.fop.pdf.PDFGState; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFImageXObject; @@ -89,11 +90,18 @@ import org.apache.fop.pdf.PDFPaintingState; import org.apache.fop.pdf.PDFPattern; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFResources; +import org.apache.fop.pdf.PDFShading; import org.apache.fop.pdf.PDFText; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter; import org.apache.fop.render.pdf.ImageRawJPEGAdapter; import org.apache.fop.render.pdf.ImageRenderedAdapter; +import org.apache.fop.render.shading.Function; +import org.apache.fop.render.shading.GradientFactory; +import org.apache.fop.render.shading.GradientRegistrar; +import org.apache.fop.render.shading.PDFGradientFactory; +import org.apache.fop.render.shading.Pattern; +import org.apache.fop.render.shading.Shading; /** *

PDF Graphics 2D. @@ -104,7 +112,7 @@ import org.apache.fop.render.pdf.ImageRenderedAdapter; * * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D */ -public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler { +public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler, GradientRegistrar { private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform(); /** The number of decimal places. */ @@ -868,11 +876,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } //Gradients are currently restricted to sRGB - PDFDeviceColorSpace aColorSpace; - aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); - PDFPattern myPat = pdfDoc.getFactory().makeGradient( - resourceContext, false, aColorSpace, - someColors, theBounds, theCoords, theMatrix); + PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); + PDFGradientFactory gradientFactory = (PDFGradientFactory)GradientFactory.newInstance(this); + PDFPattern myPat = gradientFactory.createGradient(false, colSpace, someColors, theBounds, + theCoords, theMatrix); currentStream.write(myPat.getColorSpaceOut(fill)); return true; @@ -944,13 +951,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand float offset = fractions[count]; theBounds.add(new Double(offset)); } - PDFDeviceColorSpace colSpace; - colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); - - PDFPattern myPat = pdfDoc.getFactory().makeGradient( - resourceContext, true, colSpace, - someColors, theBounds, theCoords, theMatrix); - + PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); + PDFGradientFactory gradientFactory = (PDFGradientFactory) GradientFactory.newInstance(this); + PDFPattern myPat = gradientFactory.createGradient(true, colSpace, someColors, theBounds, + theCoords, theMatrix); currentStream.write(myPat.getColorSpaceOut(fill)); return true; @@ -1856,4 +1860,36 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand //NYI } + /** + * Registers a function object against the output format document + * @param function The function object to register + * @return Returns either the function which has already been registered + * or the current new registered object. + */ + public Function registerFunction(Function function) { + return pdfDoc.getFactory().registerFunction((PDFFunction)function); + } + + /** + * Registers a shading object against the otuput format document + * @param shading The shading object to register + * @return Returs either the shading which has already been registered + * or the current new registered object + */ + public Shading registerShading(Shading shading) { + assert shading instanceof PDFShading; + return pdfDoc.getFactory().registerShading(resourceContext, (PDFShading)shading); + } + + /** + * Registers a pattern object against the output format document + * @param pattern The pattern object to register + * @return Returns either the pattern which has already been registered + * or the current new registered object + */ + public Pattern registerPattern(Pattern pattern) { + assert pattern instanceof PDFPattern; + return pdfDoc.getFactory().registerPattern(resourceContext, (PDFPattern)pattern); + } + } diff --git a/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java b/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java new file mode 100644 index 000000000..b48da4186 --- /dev/null +++ b/test/java/org/apache/fop/render/ps/svg/PSSVGGraphics2DTestCase.java @@ -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.render.ps.svg; + +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Float; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import org.apache.commons.io.FileUtils; + +import org.apache.batik.ext.awt.RadialGradientPaint; + +import org.apache.xmlgraphics.java2d.GraphicContext; +import org.apache.xmlgraphics.ps.PSGenerator; + +import static org.junit.Assert.assertEquals; + +public class PSSVGGraphics2DTestCase { + + float cx = 841.891f; + float cy = 178.583f; + float r = 16.4331f; + float[] fractions = {0.2f, 0.6012f, 0.8094f, 1.0f}; + + /** + * Tests a radial gradient generated pattern with certain inputs against + * an expected output. + * @throws IOException + */ + @Test + public void testApplyPaint() throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PSGenerator gen = new PSGenerator(os); + PSSVGGraphics2D svgGraphics2D = new PSSVGGraphics2D(false, gen); + svgGraphics2D.setGraphicContext(new GraphicContext()); + svgGraphics2D.setTransform(new AffineTransform()); + Color[] colors = {new Color(255, 255, 255), new Color(200, 200, 200), + new Color(170, 170, 170), new Color(140, 140, 140)}; + RadialGradientPaint paint = new RadialGradientPaint(cx, cy, r, fractions, colors); + Float s = new Rectangle2D.Float(7.0f, 3.0f, 841.891f, 178.583f); + svgGraphics2D.applyPaint(paint, true); + byte[] test = os.toByteArray(); + + byte[] expected = FileUtils.readFileToByteArray( + new File("test/java/org/apache/fop/render/ps/svg/expected.ps")); + assertEquals(new String(test), new String(expected)); + } +} diff --git a/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java b/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java new file mode 100644 index 000000000..283d3f4ad --- /dev/null +++ b/test/java/org/apache/fop/render/ps/svg/PSSVGLinearGraphics2DTestCase.java @@ -0,0 +1,70 @@ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps.svg; + +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Float; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import org.apache.commons.io.FileUtils; + +import org.apache.batik.ext.awt.LinearGradientPaint; + +import org.apache.xmlgraphics.java2d.GraphicContext; +import org.apache.xmlgraphics.ps.PSGenerator; + +import static org.junit.Assert.assertEquals; + +public class PSSVGLinearGraphics2DTestCase { + float startX = 115f; + float endX = 15f; + float startY = 285f; + float endY=15f; + float[] fractions = {0.0f, 1.0f}; + + /** + * Tests a linear gradient generated pattern with certain inputs against + * an expected output. + * @throws IOException + */ + @Test + public void testApplyPaint() throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PSGenerator gen = new PSGenerator(os); + PSSVGGraphics2D svgGraphics2D = new PSSVGGraphics2D(false, gen); + svgGraphics2D.setGraphicContext(new GraphicContext()); + svgGraphics2D.setTransform(new AffineTransform()); + Color[] colors = {new Color(255, 255, 0), new Color(255, 0, 0)}; + LinearGradientPaint paint = new LinearGradientPaint(startX, startY, endX, endY, fractions, colors); + Float s = new Rectangle2D.Float(115.0f, 15.0f, 170f, 110f); + svgGraphics2D.applyPaint(paint, true); + byte[] test = os.toByteArray(); + + byte[] expected = FileUtils.readFileToByteArray( + new File("test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat")); + assertEquals(new String(test), new String(expected)); + } +} diff --git a/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat b/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat new file mode 100644 index 000000000..fdf92cda5 --- /dev/null +++ b/test/java/org/apache/fop/render/ps/svg/axial-shading-expected.dat @@ -0,0 +1,26 @@ +/Pattern setcolorspace +<< +/Type /Pattern +/PatternType 2 +/Shading << +/ShadingType 2 +/ColorSpace /DeviceRGB + /Coords [ 115 285 15 15 ] + /Domain [ 0 1 ] + /Extend [ true true ] + /Function << +/FunctionType 3 +/Domain [ 0 1 ] +/Functions [ << +/FunctionType 2 +/Domain [ 0 1 ] +/C0 [ 1 1 0 ] +/C1 [ 1 0 0 ] +/N 1 +>> ] +/Encode [ 0 1 ] +/Bounds [ ] +>> +>> +>> +matrix makepattern setcolor diff --git a/test/java/org/apache/fop/render/ps/svg/expected.ps b/test/java/org/apache/fop/render/ps/svg/expected.ps new file mode 100644 index 000000000..9977070c3 --- /dev/null +++ b/test/java/org/apache/fop/render/ps/svg/expected.ps @@ -0,0 +1,38 @@ +/Pattern setcolorspace +<< +/Type /Pattern +/PatternType 2 +/Shading << +/ShadingType 3 +/ColorSpace /DeviceRGB + /Coords [ 841.890991 178.582993 3.28662 841.890991 178.582993 16.4331 ] + /Domain [ 0 1 ] + /Extend [ true true ] + /Function << +/FunctionType 3 +/Domain [ 0 1 ] +/Functions [ << +/FunctionType 2 +/Domain [ 0 1 ] +/C0 [ 1 1 1 ] +/C1 [ 0.784314 0.784314 0.784314 ] +/N 1 +>> << +/FunctionType 2 +/Domain [ 0 1 ] +/C0 [ 0.784314 0.784314 0.784314 ] +/C1 [ 0.666667 0.666667 0.666667 ] +/N 1 +>> << +/FunctionType 2 +/Domain [ 0 1 ] +/C0 [ 0.666667 0.666667 0.666667 ] +/C1 [ 0.54902 0.54902 0.54902 ] +/N 1 +>> ] +/Encode [ 0 1 0 1 0 1 ] +/Bounds [ 0.6012 0.8094 ] +>> +>> +>> +matrix makepattern setcolor -- cgit v1.2.3 From 7a151fd401340c0c4578a4d865a3245551ff85c4 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:45:39 +0000 Subject: Code clean-up and simplification git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541863 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 8 ++++++ .../inline/InlineContainerLayoutManager.java | 32 ++++++---------------- 2 files changed, 17 insertions(+), 23 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index 13ea9943a..bcff3dc72 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -158,10 +158,18 @@ public class InlineContainer extends FObj { return this.displayAlign; } + public KeepProperty getKeepWithPrevious() { + return keepWithPrevious; + } + public KeepProperty getKeepTogether() { return keepTogether; } + public KeepProperty getKeepWithNext() { + return keepWithNext; + } + public SpaceProperty getLineHeight() { return lineHeight; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 5127794c0..771bda255 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -19,6 +19,7 @@ package org.apache.fop.layoutmgr.inline; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -86,11 +87,9 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { determineIPD(context); - LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? - childLC.setRefIPD(contentAreaIPD); - childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + childElements = getChildKnuthElements(context, alignment); determineBPD(); - alignmentContext = makeAlignmentContext(context); // TODO correct? + alignmentContext = makeAlignmentContext(context); Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); @@ -117,18 +116,16 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen List allChildElements = new LinkedList(); LayoutManager childLM; while ((childLM = getChildLM()) != null) { - LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? - childLC.setRefIPD(layoutContext.getRefIPD()); + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); + childLC.setRefIPD(contentAreaIPD); @SuppressWarnings("unchecked") List childElements = childLM.getNextKnuthElements(childLC, alignment); allChildElements.addAll(childElements); - // TODO breaks, keeps, empty content } handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); SpaceResolver.performConditionalsNotification(allChildElements, 0, allChildElements.size() - 1, -1); - // TODO break-before, break-after return allChildElements; } @@ -254,9 +251,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen assert inlineContainerPosition != null; KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); AreaAdditionUtil.addAreas(this, childPosIter, context); - -// boolean isLast = (context.isLastArea() && prevLM == lastChildLM); -// context.setFlags(LayoutContext.LAST_AREA, isLast); } @Override @@ -267,23 +261,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen TraitSetter.setProducerID(referenceArea, fobj.getId()); referenceArea.setIPD(contentAreaIPD); currentViewport = new InlineViewport(referenceArea); - currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); + TraitSetter.setProducerID(currentViewport, fobj.getId()); + currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.setIPD(getContentAreaIPD()); currentViewport.setBPD(getContentAreaBPD()); - TraitSetter.setProducerID(currentViewport, fobj.getId()); - TraitSetter.addBorders(currentViewport, - borderProps, - false, false, false, false, this); - TraitSetter.addPadding(currentViewport, - borderProps, - false, false, false, false, this); - TraitSetter.addBackground(currentViewport, - borderProps, - this); + TraitSetter.addBackground(currentViewport, borderProps, this); currentViewport.setClip(needClip()); currentViewport.setContentPosition( - new java.awt.geom.Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); + new Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); getParent().addChildArea(currentViewport); } return referenceArea; -- cgit v1.2.3 From b24031d061c8902b4aa37859250f2a1c7504ebef Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:46:24 +0000 Subject: Implemented missing methods git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541864 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/InlineContainerLayoutManager.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 771bda255..020eb9982 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -301,31 +301,30 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } public List addALetterSpaceTo(List oldList) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } public List addALetterSpaceTo(List oldList, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } public String getWordChars(Position pos) { - throw new UnsupportedOperationException("Not implemented"); + return ""; } public void hyphenate(Position pos, HyphContext hyphContext) { - throw new UnsupportedOperationException("Not implemented"); } public boolean applyChanges(List oldList) { - throw new UnsupportedOperationException("Not implemented"); + return false; } public boolean applyChanges(List oldList, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return false; } public List getChangedKnuthElements(List oldList, int alignment, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } } -- cgit v1.2.3 From a9bbaac9f246b964d95ab849d1aac999ce10a6a7 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:47:24 +0000 Subject: Fixed the setting of generates[Block|Reference]Area git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541865 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java | 3 --- src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java | 1 + src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java | 1 + .../apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java | 6 +----- src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java | 1 + 5 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java index 5d7cc0b64..635e267c1 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java @@ -65,9 +65,6 @@ public abstract class AbstractBaseLayoutManager public AbstractBaseLayoutManager(FObj fo) { this.fobj = fo; setGeneratesReferenceArea(fo.generatesReferenceAreas()); - if (getGeneratesReferenceArea()) { - setGeneratesBlockArea(true); - } } // --------- Property Resolution related functions --------- // diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 83990797b..3ab267d43 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -90,6 +90,7 @@ public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayout */ public BlockContainerLayoutManager(BlockContainer node) { super(node); + setGeneratesBlockArea(true); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 0260046e1..4c7afa8d6 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -58,6 +58,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager */ public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) { super(node); + setGeneratesBlockArea(true); setParent(pslm); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 020eb9982..54237a914 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -71,6 +71,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen public InlineContainerLayoutManager(InlineContainer node) { super(node); + setGeneratesReferenceArea(true); } @Override @@ -231,11 +232,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - @Override - public boolean getGeneratesReferenceArea() { - return true; - } - @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index c8f2cea85..2122df5ae 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -117,6 +117,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager */ public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) { super(node); + setGeneratesBlockArea(true); this.primaryGridUnit = pgu; this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader || node.getParent() instanceof TableHeader; -- cgit v1.2.3 From 2318d09b63a801f5d490d688c8636912aa785eb1 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Fri, 15 Nov 2013 07:13:53 +0000 Subject: FOP-2312: font-base configuration setting not working as expected Use the config file URI to resolve relative / URIs. Fall back to default base URI only if the config input stream has no URI. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1542190 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/apps/FopConfParser.java | 53 +++++++++++++++------- .../org/apache/fop/cli/CommandLineOptions.java | 7 +-- .../apache/fop/fonts/FontManagerConfigurator.java | 16 ++++--- test/config/relative-uri/base_font.xconf | 5 ++ test/config/relative-uri/base_no-font.xconf | 4 ++ test/config/relative-uri/no-base_font.xconf | 4 ++ test/config/relative-uri/no-base_no-font.xconf | 3 ++ .../org/apache/fop/apps/FopConfParserTestCase.java | 38 ++++++++++++++++ 8 files changed, 101 insertions(+), 29 deletions(-) create mode 100644 test/config/relative-uri/base_font.xconf create mode 100644 test/config/relative-uri/base_no-font.xconf create mode 100644 test/config/relative-uri/no-base_font.xconf create mode 100644 test/config/relative-uri/no-base_no-font.xconf (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/apps/FopConfParser.java b/src/java/org/apache/fop/apps/FopConfParser.java index b4918ef30..b0fa40f45 100644 --- a/src/java/org/apache/fop/apps/FopConfParser.java +++ b/src/java/org/apache/fop/apps/FopConfParser.java @@ -70,16 +70,7 @@ public class FopConfParser { */ public FopConfParser(InputStream fopConfStream, EnvironmentProfile enviro) throws SAXException, IOException { - DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); - Configuration cfg; - try { - cfg = cfgBuilder.build(fopConfStream); - } catch (ConfigurationException e) { - throw new FOPException(e); - } - // The default base URI is taken from the directory in which the fopConf resides - fopFactoryBuilder = new FopFactoryBuilder(enviro).setConfiguration(cfg); - configure(enviro.getDefaultBaseURI(), enviro.getResourceResolver(), cfg); + this(fopConfStream, enviro.getDefaultBaseURI(), enviro); } /** @@ -94,7 +85,8 @@ public class FopConfParser { */ public FopConfParser(InputStream fopConfStream, URI defaultBaseURI, ResourceResolver resourceResolver) throws SAXException, IOException { - this(fopConfStream, EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver)); + this(fopConfStream, defaultBaseURI, + EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver)); } /** @@ -122,6 +114,20 @@ public class FopConfParser { this(fopConfFile, ResourceResolverFactory.createDefaultResourceResolver()); } + /** + * Constructor that takes the FOP conf and a default base URI and uses the default URI resolver. + * + * @param fopConfFile the FOP conf file + * @param defaultBaseURI the default base URI + * @throws SAXException if a SAX error was thrown parsing the FOP conf + * @throws IOException if an I/O error is thrown while parsing the FOP conf + */ + public FopConfParser(File fopConfFile, URI defaultBaseURI) throws SAXException, IOException { + this(new FileInputStream(fopConfFile), fopConfFile.toURI(), + EnvironmentalProfileFactory.createDefault(defaultBaseURI, + ResourceResolverFactory.createDefaultResourceResolver())); + } + /** * Constructor that parses the FOP conf and uses the URI resolver given. * @@ -132,11 +138,24 @@ public class FopConfParser { */ public FopConfParser(File fopConfFile, ResourceResolver resourceResolver) throws SAXException, IOException { - this(new FileInputStream(fopConfFile), - fopConfFile.getAbsoluteFile().getParentFile().toURI(), resourceResolver); + this(new FileInputStream(fopConfFile), fopConfFile.toURI(), resourceResolver); + } + + private FopConfParser(InputStream fopConfStream, URI baseURI, EnvironmentProfile enviro) + throws SAXException, IOException { + DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); + Configuration cfg; + try { + cfg = cfgBuilder.build(fopConfStream); + } catch (ConfigurationException e) { + throw new FOPException(e); + } + // The default base URI is taken from the directory in which the fopConf resides + fopFactoryBuilder = new FopFactoryBuilder(enviro).setConfiguration(cfg); + configure(baseURI, enviro.getResourceResolver(), cfg); } - private void configure(final URI defaultBaseURI, final ResourceResolver resourceResolver, + private void configure(final URI baseURI, final ResourceResolver resourceResolver, Configuration cfg) throws FOPException { if (log.isDebugEnabled()) { log.debug("Initializing FopFactory Configuration"); @@ -174,7 +193,7 @@ public class FopConfParser { if (cfg.getChild("base", false) != null) { try { URI confUri = InternalResourceResolver.getBaseURI(cfg.getChild("base").getValue(null)); - fopFactoryBuilder.setBaseURI(defaultBaseURI.resolve(confUri)); + fopFactoryBuilder.setBaseURI(baseURI.resolve(confUri)); } catch (URISyntaxException use) { LogUtil.handleException(log, use, strict); } @@ -242,8 +261,8 @@ public class FopConfParser { } // configure font manager - new FontManagerConfigurator(cfg, fopFactoryBuilder.getBaseURI(), resourceResolver).configure( - fopFactoryBuilder.getFontManager(), strict); + new FontManagerConfigurator(cfg, baseURI, fopFactoryBuilder.getBaseURI(), resourceResolver) + .configure(fopFactoryBuilder.getFontManager(), strict); // configure image loader framework configureImageLoading(cfg.getChild("image-loading", false), strict); diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index 41fcdcac5..ad8019a7d 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -21,10 +21,8 @@ package org.apache.fop.cli; // java import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.PrintStream; import java.net.URI; import java.util.Locale; @@ -47,7 +45,6 @@ import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.FopFactoryBuilder; import org.apache.fop.apps.FopFactoryConfig; import org.apache.fop.apps.MimeConstants; -import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.pdf.PDFAMode; import org.apache.fop.pdf.PDFEncryptionManager; import org.apache.fop.pdf.PDFEncryptionParams; @@ -1018,9 +1015,7 @@ public class CommandLineOptions { fopFactoryBuilder.setComplexScriptFeatures(useComplexScriptFeatures); } else { try { - InputStream userConfig = new FileInputStream(userConfigFile); - FopConfParser fopConfParser = new FopConfParser(userConfig, baseURI, - ResourceResolverFactory.createDefaultResourceResolver()); + FopConfParser fopConfParser = new FopConfParser(userConfigFile, baseURI); fopFactoryBuilder = fopConfParser.getFopFactoryBuilder(); } catch (SAXException e) { throw new FOPException(e); diff --git a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java index fc2ce06a6..72c1684b6 100644 --- a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java +++ b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java @@ -48,20 +48,24 @@ public class FontManagerConfigurator { private final Configuration cfg; - private final URI defaultBaseUri; + private final URI baseURI; + + private final URI fallbackURI; private final ResourceResolver resourceResolver; /** * Main constructor * @param cfg the font manager configuration object - * @param defaultBaseUri the default URI base to use for URI resolution + * @param baseURI the URI against which to resolve relative URIs + * @param fallbackURI the URI to use as a fallback if font-base is unspecified * @param resourceResolver the resource resolver */ - public FontManagerConfigurator(Configuration cfg, URI defaultBaseUri, + public FontManagerConfigurator(Configuration cfg, URI baseURI, URI fallbackURI, ResourceResolver resourceResolver) { this.cfg = cfg; - this.defaultBaseUri = defaultBaseUri; + this.baseURI = baseURI; + this.fallbackURI = fallbackURI; this.resourceResolver = resourceResolver; } @@ -77,13 +81,13 @@ public class FontManagerConfigurator { URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("font-base") .getValue(null)); fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( - defaultBaseUri.resolve(fontBase), resourceResolver)); + baseURI.resolve(fontBase), resourceResolver)); } catch (URISyntaxException use) { LogUtil.handleException(log, use, true); } } else { fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver( - defaultBaseUri, resourceResolver)); + fallbackURI, resourceResolver)); } // caching (fonts) if (cfg.getChild("use-cache", false) != null) { diff --git a/test/config/relative-uri/base_font.xconf b/test/config/relative-uri/base_font.xconf new file mode 100644 index 000000000..872c102e7 --- /dev/null +++ b/test/config/relative-uri/base_font.xconf @@ -0,0 +1,5 @@ + + + relative/ + fonts/ + diff --git a/test/config/relative-uri/base_no-font.xconf b/test/config/relative-uri/base_no-font.xconf new file mode 100644 index 000000000..97d378511 --- /dev/null +++ b/test/config/relative-uri/base_no-font.xconf @@ -0,0 +1,4 @@ + + + relative/ + diff --git a/test/config/relative-uri/no-base_font.xconf b/test/config/relative-uri/no-base_font.xconf new file mode 100644 index 000000000..165cc1231 --- /dev/null +++ b/test/config/relative-uri/no-base_font.xconf @@ -0,0 +1,4 @@ + + + fonts/ + diff --git a/test/config/relative-uri/no-base_no-font.xconf b/test/config/relative-uri/no-base_no-font.xconf new file mode 100644 index 000000000..211d94212 --- /dev/null +++ b/test/config/relative-uri/no-base_no-font.xconf @@ -0,0 +1,3 @@ + + + diff --git a/test/java/org/apache/fop/apps/FopConfParserTestCase.java b/test/java/org/apache/fop/apps/FopConfParserTestCase.java index 3c2930942..dc794fdb8 100644 --- a/test/java/org/apache/fop/apps/FopConfParserTestCase.java +++ b/test/java/org/apache/fop/apps/FopConfParserTestCase.java @@ -19,6 +19,7 @@ package org.apache.fop.apps; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -127,4 +128,41 @@ public class FopConfParserTestCase { builder.setPreferRenderer(true); assertTrue(buildFactory().getRendererFactory().isRendererPreferred()); } + + @Test + public void testRelativeURINoBaseNoFont() throws Exception { + checkRelativeURIs("test/config/relative-uri/no-base_no-font.xconf", + "", ""); + } + + @Test + public void testRelativeURINoBaseFont() throws Exception { + checkRelativeURIs("test/config/relative-uri/no-base_font.xconf", + "", "test/config/relative-uri/fonts/"); + } + + @Test + public void testRelativeURIBaseNoFont() throws Exception { + checkRelativeURIs("test/config/relative-uri/base_no-font.xconf", + "test/config/relative-uri/relative/", "test/config/relative-uri/relative/"); + } + + @Test + public void testRelativeURIBaseFont() throws Exception { + checkRelativeURIs("test/config/relative-uri/base_font.xconf", + "test/config/relative-uri/relative/", "test/config/relative-uri/fonts/"); + } + + private void checkRelativeURIs(String conf, String expectedBase, String expectedFontBase) + throws SAXException, IOException { + File configFile = new File(conf); + URI currentDir = new File(".").getCanonicalFile().toURI(); + FopConfParser parser = new FopConfParser(configFile, currentDir); + FopFactoryBuilder fopFactoryBuilder = parser.getFopFactoryBuilder(); + assertEquals("base URI", currentDir.resolve(expectedBase), + fopFactoryBuilder.getBaseURI()); + assertEquals("font base", currentDir.resolve(expectedFontBase), + fopFactoryBuilder.getFontManager().getResourceResolver().getBaseURI()); + } + } -- cgit v1.2.3 From 6b16845a94f22905bab9fe9797f00c924aef7ef7 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 19 Nov 2013 20:20:11 +0000 Subject: Restricted CSOFF filter to LineLength and fixed resulting Checkstyle issues git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1543560 13f79535-47bb-0310-9956-ffa450edef68 --- checkstyle-5.5.xml | 10 +- checkstyle-suppressions.xml | 9 +- .../fop/complexscripts/bidi/GenerateBidiClass.java | 13 ++- .../complexscripts/bidi/GenerateBidiTestData.java | 48 +++++---- .../apache/fop/complexscripts/bidi/BidiClass.java | 1 - .../fop/complexscripts/bidi/BidiResolver.java | 10 +- .../complexscripts/bidi/DelimitedTextRange.java | 5 +- .../apache/fop/complexscripts/bidi/InlineRun.java | 11 +-- .../fop/complexscripts/bidi/TextInterval.java | 5 +- .../complexscripts/bidi/UnflattenProcessor.java | 29 +++--- .../complexscripts/bidi/UnicodeBidiAlgorithm.java | 69 ++++++------- .../fop/complexscripts/fonts/GlyphClassTable.java | 11 +-- .../complexscripts/fonts/GlyphCoverageTable.java | 8 +- .../fonts/GlyphDefinitionSubtable.java | 1 - .../complexscripts/fonts/GlyphDefinitionTable.java | 1 - .../complexscripts/fonts/GlyphMappingTable.java | 4 +- .../fonts/GlyphPositioningState.java | 1 - .../fonts/GlyphPositioningSubtable.java | 8 +- .../fonts/GlyphPositioningTable.java | 107 ++++++++++----------- .../complexscripts/fonts/GlyphProcessingState.java | 21 ++-- .../fonts/GlyphSubstitutionState.java | 3 +- .../fonts/GlyphSubstitutionSubtable.java | 7 +- .../fonts/GlyphSubstitutionTable.java | 30 +++--- .../fop/complexscripts/fonts/GlyphSubtable.java | 1 - .../fop/complexscripts/fonts/GlyphTable.java | 49 ++++------ .../fonts/OTFAdvancedTypographicTableReader.java | 6 +- .../scripts/ArabicScriptProcessor.java | 22 ++--- .../scripts/DevanagariScriptProcessor.java | 27 +++--- .../scripts/GujaratiScriptProcessor.java | 27 +++--- .../scripts/GurmukhiScriptProcessor.java | 27 +++--- .../scripts/IndicScriptProcessor.java | 17 +--- .../complexscripts/scripts/ScriptProcessor.java | 12 +-- .../apache/fop/complexscripts/util/CharScript.java | 38 +++----- .../fop/complexscripts/util/GlyphSequence.java | 19 ++-- .../fop/complexscripts/util/NumberConverter.java | 39 +++----- .../org/apache/fop/complexscripts/util/UTF32.java | 1 - src/java/org/apache/fop/fonts/apps/TTFReader.java | 1 - .../apache/fop/text/linebreak/LineBreakUtils.java | 11 +-- .../fop/complexscripts/bidi/BidiTestData.java | 1 - .../fop/complexscripts/fonts/ttx/TTXFile.java | 2 - 40 files changed, 291 insertions(+), 421 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/checkstyle-5.5.xml b/checkstyle-5.5.xml index fb82878fc..30a39612e 100644 --- a/checkstyle-5.5.xml +++ b/checkstyle-5.5.xml @@ -37,12 +37,16 @@ - + + + + + @@ -172,10 +176,6 @@ - - - - diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml index 76e282f61..7dc583dbb 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -1,8 +1,9 @@ - - - - + + + + + diff --git a/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiClass.java b/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiClass.java index 506629ab2..c6845fb1d 100644 --- a/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiClass.java +++ b/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiClass.java @@ -32,7 +32,6 @@ import java.util.TreeSet; import org.apache.fop.util.License; // CSOFF: LineLength -// CSOFF: NoWhitespaceAfter /** *

Utility for generating a Java class representing bidirectional @@ -303,7 +302,7 @@ public final class GenerateBidiClass { sb.setLength(0); out.println("private static byte[] bcL1 = {"); for (int i = 0; i < bcL1.length; i++) { - if (! first) { + if (!first) { sb.append(","); } else { first = false; @@ -327,7 +326,7 @@ public final class GenerateBidiClass { sb.setLength(0); out.println("private static byte[] bcR1 = {"); for (int i = 0; i < bcR1.length; i++) { - if (! first) { + if (!first) { sb.append(","); } else { first = false; @@ -351,7 +350,7 @@ public final class GenerateBidiClass { sb.setLength(0); out.println("private static int[] bcS1 = {"); for (int i = 0; i < bcS1.length; i++) { - if (! first) { + if (!first) { sb.append(","); } else { first = false; @@ -375,7 +374,7 @@ public final class GenerateBidiClass { sb.setLength(0); out.println("private static int[] bcE1 = {"); for (int i = 0; i < bcE1.length; i++) { - if (! first) { + if (!first) { sb.append(","); } else { first = false; @@ -399,7 +398,7 @@ public final class GenerateBidiClass { sb.setLength(0); out.println("private static byte[] bcC1 = {"); for (int i = 0; i < bcC1.length; i++) { - if (! first) { + if (!first) { sb.append(","); } else { first = false; @@ -434,7 +433,7 @@ public final class GenerateBidiClass { if (k >= 0) { return ca [ k ]; } else { - k = - (k + 1); + k = -(k + 1); if (k == 0) { return BidiConstants.L; } else if (ch <= ea [ k - 1 ]) { diff --git a/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiTestData.java b/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiTestData.java index 0c02e305e..d13ab2534 100644 --- a/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiTestData.java +++ b/src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/GenerateBidiTestData.java @@ -40,10 +40,6 @@ import java.util.TreeMap; import org.apache.fop.util.License; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck -// CSOFF: EmptyForIteratorPadCheck /** *

Utility for generating a Java class and associated data files representing @@ -202,7 +198,7 @@ public final class GenerateBidiTestData { String ucName = sa[1]; if (isBlockStart(ucName)) { String ucBlock = getBlockName(ucName); - if (! im.containsKey(ucBlock)) { + if (!im.containsKey(ucBlock)) { im.put(ucBlock, new int[] { uc, -1, bc }); } else { throw new IllegalArgumentException("duplicate start of block '" + ucBlock + "' at entry: " + line); @@ -223,7 +219,7 @@ public final class GenerateBidiTestData { } else { Integer k = Integer.valueOf(bc); List sl; - if (! sm.containsKey(k)) { + if (!sm.containsKey(k)) { sl = new ArrayList(); sm.put(k, sl); } else { @@ -267,7 +263,7 @@ public final class GenerateBidiTestData { } } // populate intervals from (block) interval map - if (! im.isEmpty()) { + if (!im.isEmpty()) { for (Iterator it = im.values().iterator(); it.hasNext(); ) { int[] ba = (int[]) it.next(); assert (ba != null) && (ba.length > 2); @@ -335,7 +331,7 @@ public final class GenerateBidiTestData { continue; } else if (line.startsWith("#")) { continue; - } else if (line.startsWith(PFX_TYPE) && ! ignoreDeprecatedTypeData) { + } else if (line.startsWith(PFX_TYPE) && !ignoreDeprecatedTypeData) { List lines = new ArrayList(); if ((n = readType(line, b, lines)) < 0) { break; @@ -356,14 +352,14 @@ public final class GenerateBidiTestData { // dump instrumentation if (verbose) { System.out.println(); - if (! ignoreDeprecatedTypeData) { + if (!ignoreDeprecatedTypeData) { System.out.println("Read type ranges : " + numTypeRanges); } System.out.println("Read level specs : " + numLevelSpecs); System.out.println("Read test specs : " + numTestSpecs); System.out.println("Read lines : " + lineNumber); } - if (! ignoreDeprecatedTypeData) { + if (!ignoreDeprecatedTypeData) { td = (int[][]) tdl.toArray(new int [ tdl.size() ] []); } ld = (int[][]) ldl.toArray(new int [ ldl.size() ] []); @@ -378,12 +374,12 @@ public final class GenerateBidiTestData { boolean done = false; int n = 0; lines.add(line); - while (! done) { + while (!done) { switch (testPrefix(b, PFX_LEVELS)) { case 0: // within current levels if ((line = b.readLine()) != null) { n++; - if ((line.length() > 0) && ! line.startsWith("#")) { + if ((line.length() > 0) && !line.startsWith("#")) { lines.add(line); } } else { @@ -509,7 +505,7 @@ public final class GenerateBidiTestData { CharacterIterator ci = new StringCharacterIterator(charRanges); // read initial list delimiter skipSpace(ci); - if (! readStartOfList(ci)) { + if (!readStartOfList(ci)) { badRangeSpec("missing initial list delimiter", charRanges); } // read negation token if present @@ -538,7 +534,7 @@ public final class GenerateBidiTestData { String s; skipSpace(ci); if ((s = maybeReadSeparator(ci)) != null) { - if ((s.length() != 0) && ! s.equals("||")) { + if ((s.length() != 0) && !s.equals("||")) { badRangeSpec("invalid item separator \"" + s + "\"", charRanges); } } @@ -557,10 +553,10 @@ public final class GenerateBidiTestData { } // read terminating list delimiter skipSpace(ci); - if (! readEndOfList(ci)) { + if (!readEndOfList(ci)) { badRangeSpec("missing terminating list delimiter", charRanges); } - if (! atEnd(ci)) { + if (!atEnd(ci)) { badRangeSpec("extraneous content prior to end of line", ci); } if (negated) { @@ -578,9 +574,9 @@ public final class GenerateBidiTestData { } private static void skipSpace(CharacterIterator ci) { - while (! atEnd(ci)) { + while (!atEnd(ci)) { char c = ci.current(); - if (! Character.isWhitespace(c)) { + if (!Character.isWhitespace(c)) { break; } else { ci.next(); @@ -589,7 +585,7 @@ public final class GenerateBidiTestData { } private static boolean maybeReadNext(CharacterIterator ci, char next) { - while (! atEnd(ci)) { + while (!atEnd(ci)) { char c = ci.current(); if (c == next) { ci.next(); @@ -906,7 +902,7 @@ public final class GenerateBidiTestData { List ll = new ArrayList(); // read prefix skipSpace(ci); - if (! maybeReadToken(ci, PFX_LEVELS)) { + if (!maybeReadToken(ci, PFX_LEVELS)) { badLevelSpec("missing prefix \"" + PFX_LEVELS + "\"", ci); } // read level values @@ -924,7 +920,7 @@ public final class GenerateBidiTestData { } // read to end of line skipSpace(ci); - if (! atEnd(ci)) { + if (!atEnd(ci)) { badLevelSpec("extraneous content prior to end of line", ci); } if (ll.size() == 0) { @@ -1007,7 +1003,7 @@ public final class GenerateBidiTestData { List rl = new ArrayList(); // read prefix skipSpace(ci); - if (! maybeReadToken(ci, PFX_REORDER)) { + if (!maybeReadToken(ci, PFX_REORDER)) { badReorderSpec("missing prefix \"" + PFX_REORDER + "\"", ci); } // read reorder values @@ -1023,7 +1019,7 @@ public final class GenerateBidiTestData { } // read to end of line skipSpace(ci); - if (! atEnd(ci)) { + if (!atEnd(ci)) { badReorderSpec("extraneous content prior to end of line", ci); } return createReorderArray(rl, levels); @@ -1066,7 +1062,7 @@ public final class GenerateBidiTestData { CharacterIterator ci = new StringCharacterIterator(line); List cl = new ArrayList(); // read bidi class identifier sequence - while (! atEnd(ci) && ! maybeReadNext(ci, ';')) { + while (!atEnd(ci) && !maybeReadNext(ci, ';')) { skipSpace(ci); int bc; if ((bc = maybeReadBidiClass(ci)) >= 0) { @@ -1086,7 +1082,7 @@ public final class GenerateBidiTestData { } // read to end of line skipSpace(ci); - if (! atEnd(ci)) { + if (!atEnd(ci)) { badTestSpec("extraneous content prior to end of line", ci); } return createTestArray(cl, bs, levels); @@ -1244,7 +1240,7 @@ public final class GenerateBidiTestData { ok = false; } } - if (! ok) { + if (!ok) { System.out.println("Usage: GenerateBidiTestData [-v] [-i] [-d ] [-b ] [-o ]"); System.out.println(" defaults:"); if (ignoreDeprecatedTypeData) { diff --git a/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java b/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java index eed7b983b..4267e275a 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java +++ b/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java @@ -21,7 +21,6 @@ package org.apache.fop.complexscripts.bidi; import java.util.Arrays; -// CSOFF: WhitespaceAfterCheck // CSOFF: LineLengthCheck /* diff --git a/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java b/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java index 8b78615d4..f1a4d2a69 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java +++ b/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java @@ -32,11 +32,7 @@ import org.apache.fop.area.LineArea; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.fo.pagination.PageSequence; -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: SimplifyBooleanReturnCheck /** *

A utility class for performing bidirectional resolution processing.

@@ -140,7 +136,7 @@ public final class BidiResolver { runsNew.addAll(ir.split()); } } - if (! runsNew.equals(runs)) { + if (!runsNew.equals(runs)) { runs = runsNew; } return runs; @@ -180,7 +176,7 @@ public final class BidiResolver { i = e - 1; } } - if (! runsNew.equals(runs)) { + if (!runsNew.equals(runs)) { runs = runsNew; } return runs; @@ -232,7 +228,7 @@ public final class BidiResolver { Vector rv = new Vector(); for (Iterator it = ranges.iterator(); it.hasNext(); ) { DelimitedTextRange r = (DelimitedTextRange) it.next(); - if (! r.isEmpty()) { + if (!r.isEmpty()) { rv.add(r); } } diff --git a/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java b/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java index 2f69eb73b..67bb0aae1 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java +++ b/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java @@ -34,10 +34,7 @@ import org.apache.fop.traits.WritingModeTraits; import org.apache.fop.traits.WritingModeTraitsGetter; import org.apache.fop.util.CharUtilities; -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** * The DelimitedTextRange class implements the "delimited text range" as described @@ -151,7 +148,7 @@ public class DelimitedTextRange { TextInterval ti = (TextInterval) it.next(); intervalsNew.addAll(assignLevels(ti, levels)); } - if (! intervalsNew.equals(intervals)) { + if (!intervalsNew.equals(intervals)) { intervals = intervalsNew; } } diff --git a/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java b/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java index c51d95c6e..f8f67f953 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java +++ b/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java @@ -35,11 +35,6 @@ import org.apache.fop.area.inline.UnresolvedPageNumber; import org.apache.fop.area.inline.WordArea; import org.apache.fop.util.CharUtilities; -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: SimplifyBooleanReturnCheck - /** * The InlineRun class is a utility class, the instances of which are used * to capture a sequence of reordering levels associated with an inline area. @@ -179,7 +174,7 @@ public class InlineRun { if (inline instanceof WordArea) { WordArea w = (WordArea) inline; // if not already reversed, then reverse now - if (! w.isReversed()) { + if (!w.isReversed()) { if ((reversals & 1) != 0) { w.reverse(mirror); } else if (mirror && maybeNeedsMirroring()) { @@ -209,10 +204,8 @@ public class InlineRun { } return true; } - } else if ((ir.levels == null) && (levels == null)) { - return true; } else { - return false; + return (ir.levels == null) && (levels == null); } } else { return false; diff --git a/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java b/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java index d904aedbb..e1a42a473 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java +++ b/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java @@ -28,7 +28,6 @@ import org.apache.fop.fo.flow.Character; import org.apache.fop.fo.flow.Leader; // CSOFF: LineLengthCheck -// CSOFF: SimplifyBooleanReturnCheck /** *

The TextInterval class is a utility class, the instances of which are used @@ -102,10 +101,8 @@ class TextInterval { return false; } else if (ti.getStart() != start) { return false; - } else if (ti.getEnd() != end) { - return false; } else { - return true; + return ti.getEnd() == end; } } else { return false; diff --git a/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java b/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java index 60ad7ba5b..c511dbd70 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java +++ b/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java @@ -35,10 +35,7 @@ import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.inline.UnresolvedPageNumber; -// CSOFF: EmptyForIteratorPadCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: SimplifyBooleanReturnCheck /** *

The UnflattenProcessor class is used to reconstruct (by unflattening) a line @@ -86,10 +83,8 @@ class UnflattenProcessor { private boolean shouldFinishTextContainer(TextArea tc, InlineArea ia) { if ((tcOrig != null) && (tc != tcOrig)) { return true; - } else if ((iaLevelLast != -1) && (ia.getBidiLevel() != iaLevelLast)) { - return true; } else { - return false; + return (iaLevelLast != -1) && (ia.getBidiLevel() != iaLevelLast); } } private void finishTextContainer() { @@ -98,7 +93,7 @@ class UnflattenProcessor { private void finishTextContainer(TextArea tc, InlineArea ia) { if (tcNew != null) { updateIPD(tcNew); - if (! icNew.empty()) { + if (!icNew.empty()) { icNew.peek().addChildArea(tcNew); } else { ilNew.add(tcNew); @@ -113,12 +108,12 @@ class UnflattenProcessor { } private boolean shouldFinishInlineContainer(List ich, TextArea tc, InlineArea ia) { if ((ich == null) || ich.isEmpty()) { - return ! icOrig.empty(); + return !icOrig.empty(); } else { - if (! icOrig.empty()) { + if (!icOrig.empty()) { InlineParent ic = ich.get(0); InlineParent ic0 = icOrig.peek(); - return (ic != ic0) && ! isInlineParentOf(ic, ic0); + return (ic != ic0) && !isInlineParentOf(ic, ic0); } else { return false; } @@ -128,14 +123,14 @@ class UnflattenProcessor { finishInlineContainer(null, null, null); } private void finishInlineContainer(List ich, TextArea tc, InlineArea ia) { - if ((ich != null) && ! ich.isEmpty()) { // finish non-matching inner inline container(s) + if ((ich != null) && !ich.isEmpty()) { // finish non-matching inner inline container(s) for (Iterator it = ich.iterator(); it.hasNext(); ) { InlineParent ic = it.next(); InlineParent ic0 = icOrig.empty() ? null : icOrig.peek(); if (ic0 == null) { assert icNew.empty(); } else if (ic != ic0) { - assert ! icNew.empty(); + assert !icNew.empty(); InlineParent icO0 = icOrig.pop(); InlineParent icN0 = icNew.pop(); assert icO0 != null; @@ -145,7 +140,7 @@ class UnflattenProcessor { } else { icNew.peek().addChildArea(icN0); } - if (! icOrig.empty() && (icOrig.peek() == ic)) { + if (!icOrig.empty() && (icOrig.peek() == ic)) { break; } } else { @@ -153,7 +148,7 @@ class UnflattenProcessor { } } } else { // finish all inline containers - while (! icNew.empty()) { + while (!icNew.empty()) { InlineParent icO0 = icOrig.pop(); InlineParent icN0 = icNew.pop(); assert icO0 != null; @@ -176,8 +171,8 @@ class UnflattenProcessor { finishInlineContainer(); } private void update(List ich, TextArea tc, InlineArea ia) { - if (! alreadyUnflattened(ia)) { - if ((ich != null) && ! ich.isEmpty()) { + if (!alreadyUnflattened(ia)) { + if ((ich != null) && !ich.isEmpty()) { pushInlineContainers(ich); } if (tc != null) { @@ -345,7 +340,7 @@ class UnflattenProcessor { Area a = ia.getParentArea(); while (a != null) { if (a instanceof InlineArea) { - if ((a instanceof InlineParent) && ! (a instanceof TextArea)) { + if ((a instanceof InlineParent) && !(a instanceof TextArea)) { ich.add((InlineParent) a); } a = ((InlineArea) a) .getParentArea(); diff --git a/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java b/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java index b1234d323..694cc9245 100644 --- a/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java +++ b/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java @@ -25,12 +25,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.traits.Direction; import org.apache.fop.util.CharUtilities; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: ParameterNumberCheck /** *

The UnicodeBidiAlgorithm class implements functionality prescribed by @@ -125,47 +120,39 @@ public final class UnicodeBidiAlgorithm implements BidiConstants { case RLE: // start right-to-left embedding case LRO: // start left-to-right override case RLO: // start right-to-left override - { - int en; /* new embedding level */ - if ((bc == RLE) || (bc == RLO)) { - en = ((ec & ~OVERRIDE) + 1) | 1; - } else { - en = ((ec & ~OVERRIDE) + 2) & ~1; - } - if (en < (MAX_LEVELS + 1)) { - es [ ei++ ] = ec; - if ((bc == LRO) || (bc == RLO)) { - ec = en | OVERRIDE; - } else { - ec = en & ~OVERRIDE; - } + int en; /* new embedding level */ + if ((bc == RLE) || (bc == RLO)) { + en = ((ec & ~OVERRIDE) + 1) | 1; + } else { + en = ((ec & ~OVERRIDE) + 2) & ~1; + } + if (en < (MAX_LEVELS + 1)) { + es [ ei++ ] = ec; + if ((bc == LRO) || (bc == RLO)) { + ec = en | OVERRIDE; } else { - // max levels exceeded, so don't change level or override + ec = en & ~OVERRIDE; } - el = ec; - break; + } else { + // max levels exceeded, so don't change level or override } + el = ec; + break; case PDF: // pop directional formatting - { - el = ec; - if (ei > 0) { - ec = es [ --ei ]; - } else { - // ignore isolated PDF - } - break; + el = ec; + if (ei > 0) { + ec = es [ --ei ]; + } else { + // ignore isolated PDF } + break; case B: // paragraph separator - { - el = ec = defaultLevel; - ei = 0; - break; - } + el = ec = defaultLevel; + ei = 0; + break; default: - { - el = ec; - break; - } + el = ec; + break; } switch (bc) { case BN: @@ -608,7 +595,7 @@ public final class UnicodeBidiAlgorithm implements BidiConstants { private static boolean isRetainedFormatting(int[] ca, int s, int e) { for (int i = s; i < e; i++) { - if (! isRetainedFormatting(ca[i])) { + if (!isRetainedFormatting(ca[i])) { return false; } } @@ -680,7 +667,7 @@ public final class UnicodeBidiAlgorithm implements BidiConstants { } else { chOut = chIn; } - if (! triggered && triggersBidi(chOut)) { + if (!triggered && triggersBidi(chOut)) { triggered = true; } if ((chOut & 0xFF0000) == 0) { diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java index 62926dc5e..301ab846e 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java @@ -23,7 +23,6 @@ import java.util.Iterator; import java.util.List; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** *

Base class implementation of glyph class table.

@@ -100,7 +99,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla } else { for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); - if (! (o instanceof Integer)) { + if (!(o instanceof Integer)) { return false; } } @@ -114,7 +113,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla } else { for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); - if (! (o instanceof MappingRange)) { + if (!(o instanceof MappingRange)) { return false; } } @@ -128,7 +127,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla } else { for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); - if (! (o instanceof GlyphCoverageTable)) { + if (!(o instanceof GlyphCoverageTable)) { return false; } } @@ -197,7 +196,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla if (it.hasNext()) { Object o = it.next(); if (o instanceof Integer) { - firstGlyph = ((Integer) o) . intValue(); + firstGlyph = ((Integer) o) .intValue(); } else { throw new AdvancedTypographicTableFormatException("illegal entry, first entry must be Integer denoting first glyph value, but is: " + o); } @@ -210,7 +209,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla while (it.hasNext()) { Object o = it.next(); if (o instanceof Integer) { - int gc = ((Integer) o) . intValue(); + int gc = ((Integer) o) .intValue(); gca [ i++ ] = gc; if (gc > gcMax) { gcMax = gc; diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java index 7e2d0e818..699672439 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java @@ -27,8 +27,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; // CSOFF: LineLengthCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: NoWhitespaceAfterCheck /** *

.Base class implementation of glyph coverage table.

@@ -103,7 +101,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph } else { for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); - if (! (o instanceof Integer)) { + if (!(o instanceof Integer)) { return false; } } @@ -117,7 +115,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph } else { for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); - if (! (o instanceof MappingRange)) { + if (!(o instanceof MappingRange)) { return false; } } @@ -183,7 +181,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph for (Iterator it = entries.iterator(); it.hasNext();) { Object o = it.next(); if (o instanceof Integer) { - int gid = ((Integer) o) . intValue(); + int gid = ((Integer) o) .intValue(); if ((gid >= 0) && (gid < 65536)) { if (gid > gidMax) { map [ i++ ] = gidMax = gid; diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java index eaa16146a..3ef9a0152 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java @@ -20,7 +20,6 @@ package org.apache.fop.complexscripts.fonts; // CSOFF: LineLengthCheck -// CSOFF: InnerAssignmentCheck /** *

The GlyphDefinitionSubtable implements an abstract base of a glyph definition subtable, diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java index 0e98e4588..a5942536c 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java @@ -29,7 +29,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.scripts.ScriptProcessor; import org.apache.fop.complexscripts.util.GlyphSequence; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck /** diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java index eef49c399..410f5e544 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java @@ -23,8 +23,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck /** @@ -162,7 +160,7 @@ public class GlyphMappingTable { int mi; if ((i = Arrays.binarySearch(sa, gid)) >= 0) { mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // matches start of (some) range - } else if ((i = - (i + 1)) == 0) { + } else if ((i = -(i + 1)) == 0) { mi = -1; // precedes first range } else if (gid > ea [ --i ]) { mi = -1; // follows preceding (or last) range diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java index 6600a8256..08c533860 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java @@ -23,7 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; // CSOFF: LineLengthCheck -// CSOFF: ParameterNumberCheck /** *

The GlyphPositioningState implements an state object used during glyph positioning diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java index d269a342a..a94fac20f 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java @@ -23,8 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: ParameterNumberCheck /** *

The GlyphPositioningSubtable implements an abstract base of a glyph subtable, @@ -90,8 +88,8 @@ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements boolean appliedOneShot = false; while (ps.hasNext()) { boolean applied = false; - if (! appliedOneShot && ps.maybeApplicable()) { - for (int i = 0, n = sta.length; ! applied && (i < n); i++) { + if (!appliedOneShot && ps.maybeApplicable()) { + for (int i = 0, n = sta.length; !applied && (i < n); i++) { if (sequenceIndex < 0) { applied = ps.apply(sta [ i ]); } else if (ps.getPosition() == (sequenceStart + sequenceIndex)) { @@ -102,7 +100,7 @@ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements } } } - if (! applied || ! ps.didConsume()) { + if (!applied || !ps.didConsume()) { ps.applyDefault(); } ps.next(); diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java index b22766013..ecc933ae2 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java @@ -33,9 +33,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.GlyphTester; // CSOFF: LineLengthCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: ParameterNumberCheck /** *

The GlyphPositioningTable class is a glyph table that implements @@ -368,7 +365,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof Value[])) { + if (((o = entries.get(0)) == null) || !(o instanceof Value[])) { throw new AdvancedTypographicTableFormatException("illegal entries, single entry must be a Value[], but is: " + ((o != null) ? o.getClass() : null)); } else { Value[] va = (Value[]) o; @@ -412,7 +409,7 @@ public class GlyphPositioningTable extends GlyphTable { int offsetLast = counts[0] + counts[1]; // skip any ignored glyphs prior to first non-ignored glyph for ( ; offset < offsetLast; ++offset) { - if (! ps.isIgnoredGlyph(offset)) { + if (!ps.isIgnoredGlyph(offset)) { break; } else { ps.consume(1); @@ -429,7 +426,7 @@ public class GlyphPositioningTable extends GlyphTable { } // skip any ignored glyphs prior to second non-ignored glyph for ( ; offset < offsetLast; ++offset) { - if (! ps.isIgnoredGlyph(offset)) { + if (!ps.isIgnoredGlyph(offset)) { break; } else { ps.consume(1); @@ -513,7 +510,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof PairValues[][])) { + if (((o = entries.get(0)) == null) || !(o instanceof PairValues[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null)); } else { pvm = (PairValues[][]) o; @@ -569,27 +566,27 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt1 = (GlyphClassTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(1)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt2 = (GlyphClassTable) o; } - if (((o = entries.get(2)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(2)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nc1 = ((Integer)(o)).intValue(); } - if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(3)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nc2 = ((Integer)(o)).intValue(); } - if (((o = entries.get(4)) == null) || ! (o instanceof PairValues[][])) { + if (((o = entries.get(4)) == null) || !(o instanceof PairValues[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null)); } else { pvm = (PairValues[][]) o; @@ -633,7 +630,7 @@ public class GlyphPositioningTable extends GlyphTable { int enw = ps.getWidth(gi2); if ((exa != null) && (ena != null)) { Value v = ena.getAlignmentAdjustment(exa); - v.adjust(- enw, 0, 0, 0); + v.adjust(-enw, 0, 0, 0); if (ps.adjust(v)) { ps.setAdjusted(true); } @@ -704,10 +701,10 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof Anchor[])) { + if (((o = entries.get(0)) == null) || !(o instanceof Anchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a Anchor[], but is: " + ((o != null) ? o.getClass() : null)); - } else if ((((Anchor[]) o) . length % 2) != 0) { - throw new AdvancedTypographicTableFormatException("illegal entries, Anchor[] array must have an even number of entries, but has: " + ((Anchor[]) o) . length); + } else if ((((Anchor[]) o) .length % 2) != 0) { + throw new AdvancedTypographicTableFormatException("illegal entries, Anchor[] array must have an even number of entries, but has: " + ((Anchor[]) o) .length); } else { aa = (Anchor[]) o; } @@ -736,7 +733,7 @@ public class GlyphPositioningTable extends GlyphTable { MarkAnchor ma = getMarkAnchor(ciMark, giMark); if (ma != null) { for (int i = 0, n = ps.getPosition(); i < n; i++) { - int gi = ps.getGlyph(- (i + 1)); + int gi = ps.getGlyph(-(i + 1)); if (ps.isMark(gi)) { continue; } else { @@ -746,7 +743,7 @@ public class GlyphPositioningTable extends GlyphTable { // start experimental fix for END OF AYAH in Lateef/Scheherazade int[] aa = ps.getAdjustment(); if (aa[2] == 0) { - v.adjust(0, 0, - ps.getWidth(giMark), 0); + v.adjust(0, 0, -ps.getWidth(giMark), 0); } // end experimental fix for END OF AYAH in Lateef/Scheherazade if (ps.adjust(v)) { @@ -835,22 +832,22 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { bct = (GlyphCoverageTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = ((Integer)(o)).intValue(); } - if (((o = entries.get(2)) == null) || ! (o instanceof MarkAnchor[])) { + if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } - if (((o = entries.get(3)) == null) || ! (o instanceof Anchor[][])) { + if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null)); } else { bam = (Anchor[][]) o; @@ -881,7 +878,7 @@ public class GlyphPositioningTable extends GlyphTable { int mxc = getMaxComponentCount(); if (ma != null) { for (int i = 0, n = ps.getPosition(); i < n; i++) { - int gi = ps.getGlyph(- (i + 1)); + int gi = ps.getGlyph(-(i + 1)); if (ps.isMark(gi)) { continue; } else { @@ -989,27 +986,27 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { lct = (GlyphCoverageTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = ((Integer)(o)).intValue(); } - if (((o = entries.get(2)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(2)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { mxc = ((Integer)(o)).intValue(); } - if (((o = entries.get(3)) == null) || ! (o instanceof MarkAnchor[])) { + if (((o = entries.get(3)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } - if (((o = entries.get(4)) == null) || ! (o instanceof Anchor[][][])) { + if (((o = entries.get(4)) == null) || !(o instanceof Anchor[][][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a Anchor[][][], but is: " + ((o != null) ? o.getClass() : null)); } else { lam = (Anchor[][][]) o; @@ -1125,22 +1122,22 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { mct2 = (GlyphCoverageTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = ((Integer)(o)).intValue(); } - if (((o = entries.get(2)) == null) || ! (o instanceof MarkAnchor[])) { + if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } - if (((o = entries.get(3)) == null) || ! (o instanceof Anchor[][])) { + if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null)); } else { mam = (Anchor[][]) o; @@ -1274,7 +1271,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1367,17 +1364,17 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 3 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt = (GlyphClassTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = ((Integer)(o)).intValue(); } - if (((o = entries.get(2)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(2)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1467,7 +1464,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1585,7 +1582,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1661,27 +1658,27 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { icdt = (GlyphClassTable) o; } - if (((o = entries.get(1)) != null) && ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(1)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + o.getClass()); } else { bcdt = (GlyphClassTable) o; } - if (((o = entries.get(2)) != null) && ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(2)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an GlyphClassTable, but is: " + o.getClass()); } else { lcdt = (GlyphClassTable) o; } - if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(3)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = ((Integer)(o)).intValue(); } - if (((o = entries.get(4)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(4)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1752,7 +1749,7 @@ public class GlyphPositioningTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -2005,7 +2002,7 @@ public class GlyphPositioningTable extends GlyphTable { boolean first = true; sb.append("{ "); if (xPlacement != 0) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2013,7 +2010,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("xPlacement = " + xPlacement); } if (yPlacement != 0) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2021,7 +2018,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("yPlacement = " + yPlacement); } if (xAdvance != 0) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2029,7 +2026,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("xAdvance = " + xAdvance); } if (yAdvance != 0) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2037,7 +2034,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("yAdvance = " + yAdvance); } if (xPlaDevice != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2045,7 +2042,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("xPlaDevice = " + xPlaDevice); } if (yPlaDevice != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2053,7 +2050,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("xPlaDevice = " + yPlaDevice); } if (xAdvDevice != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2061,7 +2058,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("xAdvDevice = " + xAdvDevice); } if (yAdvDevice != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2118,7 +2115,7 @@ public class GlyphPositioningTable extends GlyphTable { boolean first = true; sb.append("{ "); if (glyph != 0) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2126,7 +2123,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("glyph = " + glyph); } if (value1 != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; @@ -2134,7 +2131,7 @@ public class GlyphPositioningTable extends GlyphTable { sb.append("value1 = " + value1); } if (value2 != null) { - if (! first) { + if (!first) { sb.append(", "); } else { first = false; diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java index 330a27593..4f6e4181c 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java @@ -29,7 +29,6 @@ import org.apache.fop.complexscripts.util.GlyphTester; import org.apache.fop.complexscripts.util.ScriptContextTester; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** *

The GlyphProcessingState implements a common, base state object used during glyph substitution @@ -459,7 +458,7 @@ public class GlyphProcessingState { int start = index + offset; if (start < 0) { throw new IndexOutOfBoundsException("will attempt index at " + start); - } else if (! reverseOrder && ((start + count) > indexLast)) { + } else if (!reverseOrder && ((start + count) > indexLast)) { throw new IndexOutOfBoundsException("will attempt index at " + (start + count)); } else if (reverseOrder && ((start + 1) < count)) { throw new IndexOutOfBoundsException("will attempt index at " + (start - count)); @@ -469,7 +468,7 @@ public class GlyphProcessingState { } else if (glyphs.length != count) { throw new IllegalArgumentException("glyphs array is non-null, but its length (" + glyphs.length + "), is not equal to count (" + count + ")"); } - if (! reverseOrder) { + if (!reverseOrder) { return getGlyphsForward(start, count, ignoreTester, glyphs, counts); } else { return getGlyphsReverse(start, count, ignoreTester, glyphs, counts); @@ -484,7 +483,7 @@ public class GlyphProcessingState { if (gi == 65535) { ignored++; } else { - if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) { + if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) { glyphs [ counted++ ] = gi; } else { ignored++; @@ -506,7 +505,7 @@ public class GlyphProcessingState { if (gi == 65535) { ignored++; } else { - if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) { + if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) { glyphs [ counted++ ] = gi; } else { ignored++; @@ -629,7 +628,7 @@ public class GlyphProcessingState { int start = index + offset; if ((start < 0) || (start > indexLast)) { return new int[] { 0, 0 }; - } else if (! reverseOrder) { + } else if (!reverseOrder) { return getGlyphsAvailableForward(start, ignoreTester); } else { return getGlyphsAvailableReverse(start, ignoreTester); @@ -731,7 +730,7 @@ public class GlyphProcessingState { int start = index + offset; if (start < 0) { throw new IndexOutOfBoundsException("will attempt index at " + start); - } else if (! reverseOrder && ((start + count) > indexLast)) { + } else if (!reverseOrder && ((start + count) > indexLast)) { throw new IndexOutOfBoundsException("will attempt index at " + (start + count)); } else if (reverseOrder && ((start + 1) < count)) { throw new IndexOutOfBoundsException("will attempt index at " + (start - count)); @@ -741,7 +740,7 @@ public class GlyphProcessingState { } else if (associations.length != count) { throw new IllegalArgumentException("associations array is non-null, but its length (" + associations.length + "), is not equal to count (" + count + ")"); } - if (! reverseOrder) { + if (!reverseOrder) { return getAssociationsForward(start, count, ignoreTester, associations, counts); } else { return getAssociationsReverse(start, count, ignoreTester, associations, counts); @@ -757,7 +756,7 @@ public class GlyphProcessingState { if (gi == 65535) { ignored++; } else { - if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) { + if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) { if (k < count) { associations [ k++ ] = getAssociation(i - index); counted++; @@ -785,7 +784,7 @@ public class GlyphProcessingState { if (gi == 65535) { ignored++; } else { - if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) { + if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) { if (k < count) { associations [ k++ ] = getAssociation(i - index); counted++; @@ -1169,7 +1168,7 @@ public class GlyphProcessingState { for (int i = 0, n = ngt; i < n; i++) { GlyphTester gt = gta [ i ]; if (gt != null) { - if (! gt.test(gi, flags)) { + if (!gt.test(gi, flags)) { return false; } } diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java index 0b599d088..108f26c82 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java @@ -27,7 +27,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** *

The GlyphSubstitutionState implements an state object used during glyph substitution @@ -130,7 +129,7 @@ public class GlyphSubstitutionState extends GlyphProcessingState { * @param predication a predication value to add to association A if predications enabled */ public void putGlyph(int glyph, GlyphSequence.CharAssociation a, Object predication) { - if (! ogb.hasRemaining()) { + if (!ogb.hasRemaining()) { ogb = growBuffer(ogb); } ogb.put(glyph); diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java index ebcf35b8d..f4e6fb924 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java @@ -23,7 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** *

The GlyphSubstitutionSubtable implements an abstract base of a glyph substitution subtable, @@ -90,8 +89,8 @@ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements boolean appliedOneShot = false; while (ss.hasNext()) { boolean applied = false; - if (! appliedOneShot && ss.maybeApplicable()) { - for (int i = 0, n = sta.length; ! applied && (i < n); i++) { + if (!appliedOneShot && ss.maybeApplicable()) { + for (int i = 0, n = sta.length; !applied && (i < n); i++) { if (sequenceIndex < 0) { applied = ss.apply(sta [ i ]); } else if (ss.getPosition() == (sequenceStart + sequenceIndex)) { @@ -102,7 +101,7 @@ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements } } } - if (! applied || ! ss.didConsume()) { + if (!applied || !ss.didConsume()) { ss.applyDefault(); } ss.next(); diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java index 18aba212c..da708bf45 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java @@ -31,9 +31,7 @@ import org.apache.fop.complexscripts.scripts.ScriptProcessor; import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.GlyphTester; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** *

The GlyphSubstitutionTable class is a glyph table that implements @@ -306,7 +304,7 @@ public class GlyphSubstitutionTable extends GlyphTable { Object o = entries.get(0); int delta = 0; if (o instanceof Integer) { - delta = ((Integer) o) . intValue(); + delta = ((Integer) o) .intValue(); } else { throw new AdvancedTypographicTableFormatException("illegal entries entry, must be Integer, but is: " + o); } @@ -440,7 +438,7 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof int[][])) { + if (((o = entries.get(0)) == null) || !(o instanceof int[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an int[][], but is: " + ((o != null) ? o.getClass() : null)); } else { gsa = (int[][]) o; @@ -801,7 +799,7 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -894,17 +892,17 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 3 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt = (GlyphClassTable) o; } - if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = ((Integer)(o)).intValue(); } - if (((o = entries.get(2)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(2)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -994,7 +992,7 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1113,7 +1111,7 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1189,27 +1187,27 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { icdt = (GlyphClassTable) o; } - if (((o = entries.get(1)) != null) && ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(1)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + o.getClass()); } else { bcdt = (GlyphClassTable) o; } - if (((o = entries.get(2)) != null) && ! (o instanceof GlyphClassTable)) { + if (((o = entries.get(2)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an GlyphClassTable, but is: " + o.getClass()); } else { lcdt = (GlyphClassTable) o; } - if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) { + if (((o = entries.get(3)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = ((Integer)(o)).intValue(); } - if (((o = entries.get(4)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(4)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; @@ -1280,7 +1278,7 @@ public class GlyphSubstitutionTable extends GlyphTable { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; - if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) { + if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java index a3cad4fd7..e5af471af 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java @@ -24,7 +24,6 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck /** diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java index 33f59194b..d130e654a 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java @@ -37,12 +37,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: ParameterNumberCheck -// CSOFF: SimplifyBooleanReturnCheck /** *

Base class for all advanced typographic glyph tables.

@@ -86,7 +81,7 @@ public class GlyphTable { * @param lookups map from lookup specs to lookup tables */ public GlyphTable(GlyphTable gdef, Map/*>*/ lookups) { - if ((gdef != null) && ! (gdef instanceof GlyphDefinitionTable)) { + if ((gdef != null) && !(gdef instanceof GlyphDefinitionTable)) { throw new AdvancedTypographicTableFormatException("bad glyph definition table"); } else if (lookups == null) { throw new AdvancedTypographicTableFormatException("lookups must be non-null map"); @@ -166,7 +161,7 @@ public class GlyphTable { * create resulting cached state. */ protected void freezeSubtables() { - if (! frozen) { + if (!frozen) { for (Iterator it = lookupTables.values().iterator(); it.hasNext(); ) { LookupTable lt = (LookupTable) it.next(); lt.freezeSubtables(lookupTables); @@ -188,18 +183,18 @@ public class GlyphTable { List/**/ matches = new ArrayList/**/(); for (Iterator it = keys.iterator(); it.hasNext();) { LookupSpec ls = (LookupSpec) it.next(); - if (! "*".equals(script)) { - if (! ls.getScript().equals(script)) { + if (!"*".equals(script)) { + if (!ls.getScript().equals(script)) { continue; } } - if (! "*".equals(language)) { - if (! ls.getLanguage().equals(language)) { + if (!"*".equals(language)) { + if (!ls.getLanguage().equals(language)) { continue; } } - if (! "*".equals(feature)) { - if (! ls.getFeature().equals(feature)) { + if (!"*".equals(feature)) { + if (!ls.getFeature().equals(feature)) { continue; } } @@ -359,17 +354,17 @@ public class GlyphTable { * @param permitWildcard if true the permit wildcard script, language, or feature */ LookupSpec(String script, String language, String feature, boolean permitEmpty, boolean permitWildcard) { - if ((script == null) || (! permitEmpty && (script.length() == 0))) { + if ((script == null) || (!permitEmpty && (script.length() == 0))) { throw new AdvancedTypographicTableFormatException("script must be non-empty string"); - } else if ((language == null) || (! permitEmpty && (language.length() == 0))) { + } else if ((language == null) || (!permitEmpty && (language.length() == 0))) { throw new AdvancedTypographicTableFormatException("language must be non-empty string"); - } else if ((feature == null) || (! permitEmpty && (feature.length() == 0))) { + } else if ((feature == null) || (!permitEmpty && (feature.length() == 0))) { throw new AdvancedTypographicTableFormatException("feature must be non-empty string"); - } else if (! permitWildcard && script.equals("*")) { + } else if (!permitWildcard && script.equals("*")) { throw new AdvancedTypographicTableFormatException("script must not be wildcard"); - } else if (! permitWildcard && language.equals("*")) { + } else if (!permitWildcard && language.equals("*")) { throw new AdvancedTypographicTableFormatException("language must not be wildcard"); - } else if (! permitWildcard && feature.equals("*")) { + } else if (!permitWildcard && feature.equals("*")) { throw new AdvancedTypographicTableFormatException("feature must not be wildcard"); } this.script = script.trim(); @@ -405,14 +400,12 @@ public class GlyphTable { public boolean equals(Object o) { if (o instanceof LookupSpec) { LookupSpec l = (LookupSpec) o; - if (! l.script.equals(script)) { + if (!l.script.equals(script)) { return false; - } else if (! l.language.equals(language)) { - return false; - } else if (! l.feature.equals(feature)) { + } else if (!l.language.equals(language)) { return false; } else { - return true; + return l.feature.equals(feature); } } else { return false; @@ -540,7 +533,7 @@ public class GlyphTable { } } // append at end of list - if (! added && (subtable != null)) { + if (!added && (subtable != null)) { subtables.add(subtable); added = true; } @@ -567,7 +560,7 @@ public class GlyphTable { } if (subtables.size() > 0) { GlyphSubtable st = (GlyphSubtable) subtables.get(0); - if (! st.isCompatible(subtable)) { + if (!st.isCompatible(subtable)) { throw new AdvancedTypographicTableFormatException("subtable " + subtable + " is not compatible with subtable " + st); } } @@ -580,7 +573,7 @@ public class GlyphTable { * @param lookupTables map from lookup table identifers, e.g. "lu4", to lookup tables */ public void freezeSubtables(Map/**/ lookupTables) { - if (! frozen) { + if (!frozen) { GlyphSubtable[] sta = getSubtables(); resolveLookupReferences(sta, lookupTables); this.subtablesArray = sta; @@ -1305,7 +1298,7 @@ public class GlyphTable { Class c = r0.getClass(); for (int i = 1, n = rules.length; i < n; i++) { Rule r = rules[i]; - if ((r != null) && ! c.isInstance(r)) { + if ((r != null) && !c.isInstance(r)) { throw new AdvancedTypographicTableFormatException("rules[" + i + "] is not an instance of " + c.getName()); } } diff --git a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java index 4fa6c3b62..de083a995 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java +++ b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java @@ -33,10 +33,6 @@ import org.apache.fop.fonts.truetype.OFDirTabEntry; import org.apache.fop.fonts.truetype.OFTableName; import org.apache.fop.fonts.truetype.OpenFont; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck // CSOFF: LineLengthCheck /** @@ -3791,7 +3787,7 @@ public final class OTFAdvancedTypographicTableReader { } else { boolean first = true; for (int i = 0; i < ia.length; i++) { - if (! first) { + if (!first) { sb.append(' '); } else { first = false; diff --git a/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java index 1d310b21d..5e68c8763 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java @@ -33,10 +33,6 @@ import org.apache.fop.complexscripts.util.GlyphContextTester; import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck // CSOFF: LineLengthCheck /** @@ -159,11 +155,11 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor { } else { int s = a.getStart(); int e = a.getEnd(); - if (! hasFinalPrecedingContext(ca, nc, s, e)) { + if (!hasFinalPrecedingContext(ca, nc, s, e)) { return false; } else if (forcesFinalThisContext(ca, nc, s, e)) { return true; - } else if (! hasFinalFollowingContext(ca, nc, s, e)) { + } else if (!hasFinalFollowingContext(ca, nc, s, e)) { return false; } else { return true; @@ -180,9 +176,9 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor { } else { int s = a.getStart(); int e = a.getEnd(); - if (! hasInitialPrecedingContext(ca, nc, s, e)) { + if (!hasInitialPrecedingContext(ca, nc, s, e)) { return false; - } else if (! hasInitialFollowingContext(ca, nc, s, e)) { + } else if (!hasInitialFollowingContext(ca, nc, s, e)) { return false; } else { return true; @@ -211,9 +207,9 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor { } else { int s = a.getStart(); int e = a.getEnd(); - if (! hasLigaturePrecedingContext(ca, nc, s, e)) { + if (!hasLigaturePrecedingContext(ca, nc, s, e)) { return false; - } else if (! hasLigatureFollowingContext(ca, nc, s, e)) { + } else if (!hasLigatureFollowingContext(ca, nc, s, e)) { return false; } else { return true; @@ -230,11 +226,11 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor { } else { int s = a.getStart(); int e = a.getEnd(); - if (! hasMedialPrecedingContext(ca, nc, s, e)) { + if (!hasMedialPrecedingContext(ca, nc, s, e)) { return false; - } else if (! hasMedialThisContext(ca, nc, s, e)) { + } else if (!hasMedialThisContext(ca, nc, s, e)) { return false; - } else if (! hasMedialFollowingContext(ca, nc, s, e)) { + } else if (!hasMedialFollowingContext(ca, nc, s, e)) { return false; } else { return true; diff --git a/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java index 0459863ee..e3843f444 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java @@ -24,11 +24,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.util.GlyphSequence; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: WhitespaceAfter -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck // CSOFF: LineLengthCheck /** @@ -109,7 +104,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { } private static boolean containsHalfConsonant(GlyphSequence gs, int k) { - Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half"); + Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half"); return (half != null) ? half.booleanValue() : false; } @@ -134,7 +129,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { // first candidate target is after first non-half consonant for (int i = 0; i < ng; i++) { if ((i != source) && containsConsonant(gs, i)) { - if (! containsHalfConsonant(gs, i)) { + if (!containsHalfConsonant(gs, i)) { c1 = i + 1; break; } @@ -142,7 +137,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { } // second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) { - if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) { + if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) { c2 = i + 1; } else if (containsOtherMark(gs, i)) { c2 = i; @@ -159,7 +154,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { } private static boolean containsReph(GlyphSequence gs, int k) { - Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf"); + Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf"); return (rphf != null) ? rphf.booleanValue() : false; } @@ -503,13 +498,13 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { } } static boolean isC(int c) { - return isType(c,C_C); + return isType(c, C_C); } static boolean isR(int c) { - return isType(c,C_C) && hasR(c); + return isType(c, C_C) && hasR(c); } static boolean isV(int c) { - return isType(c,C_V); + return isType(c, C_V); } static boolean isN(int c) { return c == 0x093C; @@ -518,10 +513,10 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { return c == 0x094D; } static boolean isM(int c) { - return isType(c,C_M); + return isType(c, C_M); } static boolean isPreM(int c) { - return isType(c,C_M) && hasFlag(c,C_PRE); + return isType(c, C_M) && hasFlag(c, C_PRE); } static boolean isX(int c) { switch (typeOf(c)) { @@ -535,10 +530,10 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor { } } static boolean hasR(int c) { - return hasFlag(c,C_R); + return hasFlag(c, C_R); } static boolean hasN(int c) { - return hasFlag(c,C_N); + return hasFlag(c, C_N); } } diff --git a/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java index dc97e79a8..e4519623e 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java @@ -24,11 +24,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.util.GlyphSequence; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: WhitespaceAfter -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck // CSOFF: LineLengthCheck /** @@ -109,7 +104,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { } private static boolean containsHalfConsonant(GlyphSequence gs, int k) { - Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half"); + Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half"); return (half != null) ? half.booleanValue() : false; } @@ -134,7 +129,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { // first candidate target is after first non-half consonant for (int i = 0; i < ng; i++) { if ((i != source) && containsConsonant(gs, i)) { - if (! containsHalfConsonant(gs, i)) { + if (!containsHalfConsonant(gs, i)) { c1 = i + 1; break; } @@ -142,7 +137,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { } // second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) { - if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) { + if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) { c2 = i + 1; } else if (containsOtherMark(gs, i)) { c2 = i; @@ -159,7 +154,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { } private static boolean containsReph(GlyphSequence gs, int k) { - Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf"); + Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf"); return (rphf != null) ? rphf.booleanValue() : false; } @@ -503,13 +498,13 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { } } static boolean isC(int c) { - return isType(c,C_C); + return isType(c, C_C); } static boolean isR(int c) { - return isType(c,C_C) && hasR(c); + return isType(c, C_C) && hasR(c); } static boolean isV(int c) { - return isType(c,C_V); + return isType(c, C_V); } static boolean isN(int c) { return c == 0x0ABC; @@ -518,10 +513,10 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { return c == 0x0ACD; } static boolean isM(int c) { - return isType(c,C_M); + return isType(c, C_M); } static boolean isPreM(int c) { - return isType(c,C_M) && hasFlag(c,C_PRE); + return isType(c, C_M) && hasFlag(c, C_PRE); } static boolean isX(int c) { switch (typeOf(c)) { @@ -535,10 +530,10 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor { } } static boolean hasR(int c) { - return hasFlag(c,C_R); + return hasFlag(c, C_R); } static boolean hasN(int c) { - return hasFlag(c,C_N); + return hasFlag(c, C_N); } } diff --git a/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java index 262d56864..9c4d49f59 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java @@ -25,11 +25,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable; import org.apache.fop.complexscripts.util.GlyphSequence; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: WhitespaceAfter -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck // CSOFF: LineLengthCheck /** @@ -110,7 +105,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { } private static boolean containsHalfConsonant(GlyphSequence gs, int k) { - Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half"); + Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half"); return (half != null) ? half.booleanValue() : false; } @@ -135,7 +130,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { // first candidate target is after first non-half consonant for (int i = 0; i < ng; i++) { if ((i != source) && containsConsonant(gs, i)) { - if (! containsHalfConsonant(gs, i)) { + if (!containsHalfConsonant(gs, i)) { c1 = i + 1; break; } @@ -143,7 +138,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { } // second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) { - if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) { + if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) { c2 = i + 1; } else if (containsOtherMark(gs, i)) { c2 = i; @@ -160,7 +155,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { } private static boolean containsReph(GlyphSequence gs, int k) { - Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf"); + Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf"); return (rphf != null) ? rphf.booleanValue() : false; } @@ -504,13 +499,13 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { } } static boolean isC(int c) { - return isType(c,C_C); + return isType(c, C_C); } static boolean isR(int c) { - return isType(c,C_C) && hasR(c); + return isType(c, C_C) && hasR(c); } static boolean isV(int c) { - return isType(c,C_V); + return isType(c, C_V); } static boolean isN(int c) { return c == 0x0A3C; @@ -519,10 +514,10 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { return c == 0x0A4D; } static boolean isM(int c) { - return isType(c,C_M); + return isType(c, C_M); } static boolean isPreM(int c) { - return isType(c,C_M) && hasFlag(c,C_PRE); + return isType(c, C_M) && hasFlag(c, C_PRE); } static boolean isX(int c) { switch (typeOf(c)) { @@ -536,10 +531,10 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor { } } static boolean hasR(int c) { - return hasFlag(c,C_R); + return hasFlag(c, C_R); } static boolean hasN(int c) { - return hasFlag(c,C_N); + return hasFlag(c, C_N); } @Override diff --git a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java index c16c9fe0c..fcabad396 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java @@ -36,13 +36,6 @@ import org.apache.fop.complexscripts.util.GlyphContextTester; import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: SimplifyBooleanReturnCheck -// CSOFF: EmptyForIteratorPadCheck -// CSOFF: WhitespaceAfterCheck -// CSOFF: ParameterNumberCheck // CSOFF: LineLengthCheck /** @@ -222,7 +215,7 @@ public class IndicScriptProcessor extends DefaultScriptProcessor { } private GlyphSequence[] syllabize(GlyphSequence gs, String script, String language) { - return Syllabizer.getSyllabizer(script, language, getSyllabizerClass()) . syllabize(gs); + return Syllabizer.getSyllabizer(script, language, getSyllabizerClass()) .syllabize(gs); } private GlyphSequence unsyllabize(GlyphSequence gs, GlyphSequence[] sa) { @@ -384,12 +377,10 @@ public class IndicScriptProcessor extends DefaultScriptProcessor { public boolean equals(Object o) { if (o instanceof Syllabizer) { Syllabizer s = (Syllabizer) o; - if (! s.script.equals(script)) { - return false; - } else if (! s.language.equals(language)) { + if (!s.script.equals(script)) { return false; } else { - return true; + return s.language.equals(language); } } else { return false; @@ -408,7 +399,7 @@ public class IndicScriptProcessor extends DefaultScriptProcessor { } return d; } - private static Map syllabizers = new HashMap(); + private static Map syllabizers = new HashMap(); static Syllabizer getSyllabizer(String script, String language, Class syllabizerClass) { String sid = makeSyllabizerId(script, language); Syllabizer s = syllabizers.get(sid); diff --git a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java index 72d092b12..cfcc4ff59 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java @@ -31,11 +31,7 @@ import org.apache.fop.complexscripts.util.CharScript; import org.apache.fop.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.ScriptContextTester; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: ParameterNumberCheck -// CSOFF: SimplifyBooleanReturnCheck /** *

Abstract script processor base class for which an implementation of the substitution and positioning methods @@ -278,14 +274,12 @@ public abstract class ScriptProcessor { public boolean equals(Object o) { if (o instanceof AssembledLookupsKey) { AssembledLookupsKey k = (AssembledLookupsKey) o; - if (! table.equals(k.table)) { + if (!table.equals(k.table)) { return false; - } else if (! Arrays.equals(features, k.features)) { - return false; - } else if (! lookups.equals(k.lookups)) { + } else if (!Arrays.equals(features, k.features)) { return false; } else { - return true; + return lookups.equals(k.lookups); } } else { return false; diff --git a/src/java/org/apache/fop/complexscripts/util/CharScript.java b/src/java/org/apache/fop/complexscripts/util/CharScript.java index e81313aef..420964393 100644 --- a/src/java/org/apache/fop/complexscripts/util/CharScript.java +++ b/src/java/org/apache/fop/complexscripts/util/CharScript.java @@ -28,12 +28,6 @@ import java.util.Set; import org.apache.fop.util.CharUtilities; -// CSOFF: AvoidNestedBlocksCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: LineLengthCheck -// CSOFF: SimplifyBooleanReturnCheck -// CSOFF: WhitespaceAfterCheck - /** *

Script related utilities.

* @@ -754,16 +748,14 @@ public final class CharScript { case SCRIPT_UNCODED: break; default: - { - Integer v = (Integer) e.getValue(); - assert v != null; - int c = v.intValue(); - if (c > cMax) { - cMax = c; - sMax = s; - } - break; + Integer v = (Integer) e.getValue(); + assert v != null; + int c = v.intValue(); + if (c > cMax) { + cMax = c; + sMax = s; } + break; } } if (sMax < 0) { @@ -823,7 +815,7 @@ public final class CharScript { * @return a script tag */ public static String scriptTagFromCode(int code) { - Map m = getScriptTagsMap(); + Map m = getScriptTagsMap(); if (m != null) { String tag; if ((tag = m.get(Integer.valueOf(code))) != null) { @@ -842,7 +834,7 @@ public final class CharScript { * @return a script code */ public static int scriptCodeFromTag(String tag) { - Map m = getScriptCodeMap(); + Map m = getScriptCodeMap(); if (m != null) { Integer c; if ((c = m.get(tag)) != null) { @@ -855,8 +847,8 @@ public final class CharScript { } } - private static Map scriptTagsMap = null; - private static Map scriptCodeMap = null; + private static Map scriptTagsMap = null; + private static Map scriptCodeMap = null; private static void putScriptTag(Map tm, Map cm, int code, String tag) { assert tag != null; @@ -868,8 +860,8 @@ public final class CharScript { } private static void makeScriptMaps() { - HashMap tm = new HashMap(); - HashMap cm = new HashMap(); + HashMap tm = new HashMap(); + HashMap cm = new HashMap(); putScriptTag(tm, cm, SCRIPT_HEBREW, "hebr"); putScriptTag(tm, cm, SCRIPT_MONGOLIAN, "mong"); putScriptTag(tm, cm, SCRIPT_ARABIC, "arab"); @@ -915,14 +907,14 @@ public final class CharScript { scriptCodeMap = cm; } - private static Map getScriptTagsMap() { + private static Map getScriptTagsMap() { if (scriptTagsMap == null) { makeScriptMaps(); } return scriptTagsMap; } - private static Map getScriptCodeMap() { + private static Map getScriptCodeMap() { if (scriptCodeMap == null) { makeScriptMaps(); } diff --git a/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java b/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java index 696d2c8d3..e59dc9b32 100644 --- a/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java +++ b/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java @@ -26,10 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: WhitespaceAfterCheck -// CSOFF: NoWhitespaceAfterCheck /** *

A GlyphSequence encapsulates a sequence of character codes, a sequence of glyph codes, @@ -639,10 +636,10 @@ public class GlyphSequence implements Cloneable { private final int offset; private final int count; private final int[] subIntervals; - private Map predications; + private Map predications; // class state - private static volatile Map predicationMergers; + private static volatile Map predicationMergers; interface PredicationMerger { Object merge(String key, Object v1, Object v2); @@ -724,7 +721,7 @@ public class GlyphSequence implements Cloneable { public boolean contained(int offset, int count) { int s = offset; int e = offset + count; - if (! isDisjoint()) { + if (!isDisjoint()) { int s0 = getStart(); int e0 = getEnd(); return (s0 >= s) && (e0 <= e); @@ -748,7 +745,7 @@ public class GlyphSequence implements Cloneable { */ public void setPredication(String key, Object value) { if (predications == null) { - predications = new HashMap(); + predications = new HashMap(); } if (predications != null) { predications.put(key, value); @@ -775,7 +772,7 @@ public class GlyphSequence implements Cloneable { */ public void mergePredication(String key, Object value) { if (predications == null) { - predications = new HashMap(); + predications = new HashMap(); } if (predications != null) { if (predications.containsKey(key)) { @@ -813,7 +810,7 @@ public class GlyphSequence implements Cloneable { */ public void mergePredications(CharAssociation ca) { if (ca.predications != null) { - for (Map.Entry e : ca.predications.entrySet()) { + for (Map.Entry e : ca.predications.entrySet()) { mergePredication(e.getKey(), e.getValue()); } } @@ -824,7 +821,7 @@ public class GlyphSequence implements Cloneable { try { CharAssociation ca = (CharAssociation) super.clone(); if (predications != null) { - ca.predications = new HashMap(predications); + ca.predications = new HashMap(predications); } return ca; } catch (CloneNotSupportedException e) { @@ -839,7 +836,7 @@ public class GlyphSequence implements Cloneable { */ public static void setPredicationMerger(String key, PredicationMerger pm) { if (predicationMergers == null) { - predicationMergers = new HashMap(); + predicationMergers = new HashMap(); } if (predicationMergers != null) { predicationMergers.put(key, pm); diff --git a/src/java/org/apache/fop/complexscripts/util/NumberConverter.java b/src/java/org/apache/fop/complexscripts/util/NumberConverter.java index 08b4a4434..bfb8b6571 100644 --- a/src/java/org/apache/fop/complexscripts/util/NumberConverter.java +++ b/src/java/org/apache/fop/complexscripts/util/NumberConverter.java @@ -23,9 +23,6 @@ import java.util.ArrayList; import java.util.List; // CSOFF: LineLengthCheck -// CSOFF: InnerAssignmentCheck -// CSOFF: NoWhitespaceAfterCheck -// CSOFF: AvoidNestedBlocksCheck /** *

Implementation of Number to String Conversion algorithm specified by @@ -186,10 +183,10 @@ public class NumberConverter { separators.add(token.toArray(new Integer [ token.size() ])); } } - if (! separators.isEmpty()) { + if (!separators.isEmpty()) { this.prefix = separators.remove(0); } - if (! separators.isEmpty()) { + if (!separators.isEmpty()) { this.suffix = separators.remove(separators.size() - 1); } this.separators = separators.toArray(new Integer [ separators.size() ] []); @@ -260,33 +257,27 @@ public class NumberConverter { int s = token[0].intValue(); switch (s) { case (int) '1': - { - fn = formatNumberAsDecimal(number, (int) '1', 1); - break; - } + fn = formatNumberAsDecimal(number, (int) '1', 1); + break; case (int) 'W': case (int) 'w': - { - fn = formatNumberAsWord(number, (s == (int) 'W') ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER); - break; - } + fn = formatNumberAsWord(number, (s == (int) 'W') ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER); + break; case (int) 'A': // handled as numeric sequence case (int) 'a': // handled as numeric sequence case (int) 'I': // handled as numeric special case (int) 'i': // handled as numeric special default: - { - if (isStartOfDecimalSequence(s)) { - fn = formatNumberAsDecimal(number, s, 1); - } else if (isStartOfAlphabeticSequence(s)) { - fn = formatNumberAsSequence(number, s, getSequenceBase(s), null); - } else if (isStartOfNumericSpecial(s)) { - fn = formatNumberAsSpecial(number, s); - } else { - fn = null; - } - break; + if (isStartOfDecimalSequence(s)) { + fn = formatNumberAsDecimal(number, s, 1); + } else if (isStartOfAlphabeticSequence(s)) { + fn = formatNumberAsSequence(number, s, getSequenceBase(s), null); + } else if (isStartOfNumericSpecial(s)) { + fn = formatNumberAsSpecial(number, s); + } else { + fn = null; } + break; } } else if ((token.length == 2) && (token[0] == (int) 'W') && (token[1] == (int) 'w')) { fn = formatNumberAsWord(number, Character.TITLECASE_LETTER); diff --git a/src/java/org/apache/fop/complexscripts/util/UTF32.java b/src/java/org/apache/fop/complexscripts/util/UTF32.java index 225966f54..b0f92eb88 100644 --- a/src/java/org/apache/fop/complexscripts/util/UTF32.java +++ b/src/java/org/apache/fop/complexscripts/util/UTF32.java @@ -21,7 +21,6 @@ package org.apache.fop.complexscripts.util; import org.apache.fop.util.CharUtilities; -// CSOFF: InnerAssignmentCheck /** *

UTF32 related utilities.

diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java index f8c7360a4..f63e2bb07 100644 --- a/src/java/org/apache/fop/fonts/apps/TTFReader.java +++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java @@ -39,7 +39,6 @@ import org.apache.fop.fonts.truetype.FontFileReader; import org.apache.fop.fonts.truetype.OFFontLoader; import org.apache.fop.fonts.truetype.TTFFile; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck /** diff --git a/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java b/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java index d6e19a97a..6598d1b85 100644 --- a/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java +++ b/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java @@ -29,7 +29,6 @@ package org.apache.fop.text.linebreak; * - commit BOTH changed files */ -// CSOFF: WhitespaceAfterCheck // CSOFF: LineLengthCheck /** Line breaking utilities. */ @@ -694,11 +693,11 @@ public final class LineBreakUtils { "CP", "CR", "EX", "GL", "H2", "H3", "HY", "ID", "IN", "IS", "JL", "JT", "JV", "LF", "NL", "NS", "NU", "OP", "PO", "PR", "QU", "SA", "SG", "SP", "SY", "WJ", "XX", "ZW"}; - private static String[] lineBreakPropertyLongNames = {"Ambiguous","Alphabetic","Break_Both","Break_After","Break_Before", - "Mandatory_Break","Contingent_Break","Close_Punctuation","Combining_Mark","Close_Parenthesis","Carriage_Return", - "Exclamation","Glue","H2","H3","Hyphen","Ideographic","Inseparable","Infix_Numeric","JL","JT","JV","Line_Feed", - "Next_Line","Nonstarter","Numeric","Open_Punctuation","Postfix_Numeric","Prefix_Numeric","Quotation","Complex_Context", - "Surrogate","Space","Break_Symbols","Word_Joiner","Unknown","ZWSpace"}; + private static String[] lineBreakPropertyLongNames = {"Ambiguous", "Alphabetic", "Break_Both", "Break_After", "Break_Before", + "Mandatory_Break", "Contingent_Break", "Close_Punctuation", "Combining_Mark", "Close_Parenthesis", "Carriage_Return", + "Exclamation", "Glue", "H2", "H3", "Hyphen", "Ideographic", "Inseparable", "Infix_Numeric", "JL", "JT", "JV", "Line_Feed", + "Next_Line", "Nonstarter", "Numeric", "Open_Punctuation", "Postfix_Numeric", "Prefix_Numeric", "Quotation", "Complex_Context", + "Surrogate", "Space", "Break_Symbols", "Word_Joiner", "Unknown", "ZWSpace"}; /** * Return the short name for the linebreak property corresponding diff --git a/test/java/org/apache/fop/complexscripts/bidi/BidiTestData.java b/test/java/org/apache/fop/complexscripts/bidi/BidiTestData.java index 726682ce0..6be0c1fa9 100644 --- a/test/java/org/apache/fop/complexscripts/bidi/BidiTestData.java +++ b/test/java/org/apache/fop/complexscripts/bidi/BidiTestData.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; -// CSOFF: WhitespaceAfterCheck /* * !!! THIS IS A GENERATED FILE !!! diff --git a/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java b/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java index b3e080d50..c106e65c8 100644 --- a/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java +++ b/test/java/org/apache/fop/complexscripts/fonts/ttx/TTXFile.java @@ -72,9 +72,7 @@ import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -// CSOFF: InnerAssignmentCheck // CSOFF: LineLengthCheck -// CSOFF: NoWhitespaceAfterCheck /** * This class supports a subset of the TTX file as produced by the Adobe FLEX -- cgit v1.2.3 From a8f6ee444c695c9310871dfcaf4bcc9c1aaf4cd7 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 21 Nov 2013 14:50:24 +0000 Subject: FOP-2317: crash when issuing warning for invalid property value Patch by Seifeddine Dridi git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1544199 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/FObj.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index 0d1a2a84b..8532b27da 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -149,7 +149,7 @@ public abstract class FObj extends FONode implements Constants { String attributeName = attList.getQName(i); String attributeValue = attList.getValue(i); Property prop = propertyList.getPropertyForAttribute(attList, attributeName, attributeValue); - if (prop.equals(value)) { + if (prop != null && prop.equals(value)) { return attributeName; } } -- cgit v1.2.3 From ca51f4c9117dfba007fc26b9b6ebc2c0914d9d3d Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Sun, 24 Nov 2013 00:57:26 +0000 Subject: FOP-2314: Caching xmlParserClassName in org.apache.fop.svg.SimpleSVGUserAgent; patch submitted by Gonzalo Vasquez git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1544897 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/svg/SimpleSVGUserAgent.java | 26 +++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java index 2b27945a4..132c633dc 100644 --- a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java +++ b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java @@ -23,8 +23,11 @@ import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.awt.geom.Dimension2D; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; + import org.apache.batik.bridge.UserAgentAdapter; /** @@ -79,17 +82,28 @@ public class SimpleSVGUserAgent extends UserAgentAdapter { return null; // userStyleSheetURI; } + + private static final String XML_PARSER_CLASS_NAME; + + static { + String result; + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + result = factory.newSAXParser().getXMLReader().getClass().getName(); + } catch (SAXException e) { + result = null; + } catch (ParserConfigurationException e) { + result = null; + } + XML_PARSER_CLASS_NAME = result; + } + /** * Returns the class name of the XML parser. * @return the XML parser class name */ public String getXMLParserClassName() { - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - return factory.newSAXParser().getXMLReader().getClass().getName(); - } catch (Exception e) { - return null; - } + return XML_PARSER_CLASS_NAME; } /** -- cgit v1.2.3 From 9b1bf2de4a098a494142c27f0dc3f4d2b8327f80 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Tue, 3 Dec 2013 10:05:07 +0000 Subject: FOP-2323: NPE caused by missing local subroutine index in private dictonary of OTF font git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1547330 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java index dbea48216..906fbd5e0 100644 --- a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java +++ b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -561,8 +561,12 @@ public class OTFSubSetFile extends OTFFile { int privateOffset = privateEntry.getOperands().get(1).intValue(); Map privateDICT = cffReader.getPrivateDict(privateEntry); - int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue(); - localIndexSubr = cffReader.readIndex(localSubrOffset); + if (privateDICT.get("Subrs") != null) { + int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue(); + localIndexSubr = cffReader.readIndex(localSubrOffset); + } else { + localIndexSubr = cffReader.readIndex(null); + } } globalIndexSubr = cffReader.getGlobalIndexSubr(); -- cgit v1.2.3 From 9df34113b5b0d1f44af23b053225da4428d84c4e Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 3 Dec 2013 12:44:41 +0000 Subject: Enabled LineLength check and fixed corresponding issues git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1547372 13f79535-47bb-0310-9956-ffa450edef68 --- checkstyle-5.5.xml | 6 ++++++ src/java/org/apache/fop/complexscripts/util/CharScript.java | 2 ++ src/java/org/apache/fop/fo/expr/FunctionBase.java | 3 ++- src/java/org/apache/fop/fo/expr/PropertyParser.java | 3 ++- src/java/org/apache/fop/fonts/FontTriplet.java | 3 ++- src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java | 3 ++- src/java/org/apache/fop/render/afp/AFPFontConfig.java | 4 ++-- src/java/org/apache/fop/render/afp/AFPRendererConfig.java | 3 ++- src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java | 3 ++- src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java | 9 ++++++--- .../org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java | 3 ++- 11 files changed, 30 insertions(+), 12 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/checkstyle-5.5.xml b/checkstyle-5.5.xml index 30a39612e..4d499f645 100644 --- a/checkstyle-5.5.xml +++ b/checkstyle-5.5.xml @@ -50,6 +50,12 @@ + + + + + + diff --git a/src/java/org/apache/fop/complexscripts/util/CharScript.java b/src/java/org/apache/fop/complexscripts/util/CharScript.java index 420964393..6809be609 100644 --- a/src/java/org/apache/fop/complexscripts/util/CharScript.java +++ b/src/java/org/apache/fop/complexscripts/util/CharScript.java @@ -35,6 +35,8 @@ import org.apache.fop.util.CharUtilities; */ public final class CharScript { + // CSOFF: LineLength + // // The following script codes are based on ISO 15924. Codes less than 1000 are // official assignments from 15924; those equal to or greater than 1000 are FOP diff --git a/src/java/org/apache/fop/fo/expr/FunctionBase.java b/src/java/org/apache/fop/fo/expr/FunctionBase.java index 707424b86..e5e346060 100644 --- a/src/java/org/apache/fop/fo/expr/FunctionBase.java +++ b/src/java/org/apache/fop/fo/expr/FunctionBase.java @@ -36,7 +36,8 @@ public abstract class FunctionBase implements Function { /** {@inheritDoc} */ public Property getOptionalArgDefault(int index, PropertyInfo pi) throws PropertyException { if (index >= getOptionalArgsCount()) { - PropertyException e = new PropertyException(new IndexOutOfBoundsException("illegal optional argument index")); + PropertyException e = new PropertyException( + new IndexOutOfBoundsException("illegal optional argument index")); e.setPropertyInfo(pi); throw e; } else { diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java index cac115d6e..26c8f6c1b 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyParser.java +++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java @@ -385,7 +385,8 @@ public final class PropertyParser extends PropertyTokenizer { } int numArgs = args.size(); if (numArgs < numReq) { - throw new PropertyException("Expected " + numReq + " required arguments, but only " + numArgs + " specified"); + throw new PropertyException("Expected " + numReq + " required arguments, but only " + + numArgs + " specified"); } else { for (int i = 0; i < numOpt; i++) { if (args.size() < (numReq + i + 1)) { diff --git a/src/java/org/apache/fop/fonts/FontTriplet.java b/src/java/org/apache/fop/fonts/FontTriplet.java index 2c1d89088..9392cc8b9 100644 --- a/src/java/org/apache/fop/fonts/FontTriplet.java +++ b/src/java/org/apache/fop/fonts/FontTriplet.java @@ -27,7 +27,8 @@ import java.io.Serializable; */ public class FontTriplet implements Comparable, Serializable { - public static final FontTriplet DEFAULT_FONT_TRIPLET = new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); + public static final FontTriplet DEFAULT_FONT_TRIPLET + = new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); /** serial version UID */ private static final long serialVersionUID = 1168991106658033508L; diff --git a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java index 906fbd5e0..4d0cce67a 100644 --- a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java +++ b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -451,7 +451,8 @@ public class OTFSubSetFile extends OTFFile { localUniques = foundLocalUniquesB.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()); byte[] data = charStringsIndex.getValue(gid); subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()); - subsetLocalSubrCount = foundLocalUniques.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()).size(); + subsetLocalSubrCount = foundLocalUniques.get( + subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()).size(); data = readCharStringData(data, subsetLocalSubrCount); subsetCharStringsIndex.add(data); } diff --git a/src/java/org/apache/fop/render/afp/AFPFontConfig.java b/src/java/org/apache/fop/render/afp/AFPFontConfig.java index aef0b666c..149973edd 100644 --- a/src/java/org/apache/fop/render/afp/AFPFontConfig.java +++ b/src/java/org/apache/fop/render/afp/AFPFontConfig.java @@ -316,8 +316,8 @@ public final class AFPFontConfig implements FontConfig { private final String characterset; - private CIDKeyedFontConfig(List triplets, String type, String codePage, - String encoding, String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) { + private CIDKeyedFontConfig(List triplets, String type, String codePage, String encoding, + String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) { super(triplets, type, codePage, encoding, name, embeddable, uri); this.characterset = characterset; this.charsetType = charsetType; diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfig.java b/src/java/org/apache/fop/render/afp/AFPRendererConfig.java index 90cc6e767..a943f5aad 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfig.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfig.java @@ -103,7 +103,8 @@ public final class AFPRendererConfig implements RendererConfig { } } - private final EnumMap params = new EnumMap(AFPRendererOption.class); + private final EnumMap params + = new EnumMap(AFPRendererOption.class); private final EnumMap imageModeParams = new EnumMap(ImagesModeOptions.class); diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index d27fead12..4352dae6c 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -463,7 +463,8 @@ class PDFRenderingUtil { private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) { for (PDFCollectionEntryExtension entry : extension.getEntries()) { if (entry instanceof PDFDictionaryExtension) { - dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + dictionary.put(entry.getKey(), + augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); } else if (entry instanceof PDFArrayExtension) { dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry)); } else { diff --git a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java index 3b6b5490c..1c15e569b 100644 --- a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java @@ -119,7 +119,8 @@ public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar { List theCoords = new java.util.ArrayList(); - AffineTransform start = applyTransform(lgp.getTransform(), lgp.getStartPoint().getX(), lgp.getStartPoint().getY()); + AffineTransform start = applyTransform(lgp.getTransform(), + lgp.getStartPoint().getX(), lgp.getStartPoint().getY()); AffineTransform end = applyTransform(lgp.getTransform(), lgp.getEndPoint().getX(), lgp.getEndPoint().getY()); double startX = start.getTranslateX(); double startY = start.getTranslateY(); @@ -177,8 +178,10 @@ public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar { transform.concatenate(getTransform()); transform.concatenate(rgp.getTransform()); - AffineTransform resultCentre = applyTransform(rgp.getTransform(), rgp.getCenterPoint().getX(), rgp.getCenterPoint().getY()); - AffineTransform resultFocus = applyTransform(rgp.getTransform(), rgp.getFocusPoint().getX(), rgp.getFocusPoint().getY()); + AffineTransform resultCentre = applyTransform(rgp.getTransform(), + rgp.getCenterPoint().getX(), rgp.getCenterPoint().getY()); + AffineTransform resultFocus = applyTransform(rgp.getTransform(), + rgp.getFocusPoint().getX(), rgp.getFocusPoint().getY()); double scale = Math.sqrt(rgp.getTransform().getDeterminant()); double radius = rgp.getRadius() * scale; double centreX = resultCentre.getTranslateX(); diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java index 0c0335a8b..6f2d4429d 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java @@ -88,7 +88,8 @@ public class PDFDocumentGraphics2DConfigurator { final boolean strict = false; if (cfg != null) { URI thisUri = new File(".").getAbsoluteFile().toURI(); - InternalResourceResolver resourceResolver = ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri); + InternalResourceResolver resourceResolver + = ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri); //TODO The following could be optimized by retaining the FontManager somewhere FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(), FontCacheManagerFactory.createDefault()); -- cgit v1.2.3 From 922d33c57d1ecf805bd42971d9db3bb90af23081 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Wed, 4 Dec 2013 16:36:05 +0000 Subject: FOP-1069: No error message on illegal/unknown values on a property git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1547838 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/properties/GenericShorthandParser.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java index 5c7bf4177..140b96aac 100644 --- a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java +++ b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java @@ -83,14 +83,17 @@ public class GenericShorthandParser implements ShorthandParser { PropertyList propertyList) throws PropertyException { Property prop = null; + String vProperty = ""; // Try each of the stored values in turn Iterator iprop = property.getList().iterator(); while (iprop.hasNext() && prop == null) { Property p = (Property)iprop.next(); + if (p.getNCname() != null) { + vProperty += p.getNCname() + " "; + } prop = maker.convertShorthandProperty(propertyList, p, null); - // The following produces a regression, about which see FOP-2311. - // propertyList.validatePropertyValue(p.getNCname(), prop, property); } + propertyList.validatePropertyValue(vProperty.trim(), prop, property); return prop; } -- cgit v1.2.3 From d86afe17fd341ac115a4ab9b3012d1e5e62f8e48 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Thu, 5 Dec 2013 09:57:19 +0000 Subject: FOP-2322: Type1 Font with Custom Encoding not visible in Postscript output; Patch submitted by Simon Steiner git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1548054 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/type1/Type1FontLoader.java | 16 +++++++++++++++- src/java/org/apache/fop/render/ps/PSFontUtils.java | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java index dddf2a3a3..716faa61e 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -173,7 +173,21 @@ public class Type1FontLoader extends FontLoader { addUnencodedBasedOnAFM(afm); } } else { - if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) { + if (pfm.getCharSet() == 2 && !pfm.getCharSetName().equals("Symbol")) { + int[] table = new int[256]; + String[] charNameMap = new String[256]; + int j = 0; + for (int i = pfm.getFirstChar(); i < pfm.getLastChar(); i++) { + if (j < table.length) { + table[j] = i; + table[j + 1] = i; + j += 2; + } + charNameMap[i] = String.format("x%03o", i); + } + CodePointMapping mapping = new CodePointMapping("custom", table, charNameMap); + singleFont.setEncoding(mapping); + } else if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) { singleFont.setEncoding(pfm.getCharSetName() + "Encoding"); } else { log.warn("The PFM reports an unsupported encoding (" diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index c22a3ba28..06191f84d 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -175,6 +175,15 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) { //Only out Base 14 fonts still use that + for (Typeface tf : fonts.values()) { + if (tf instanceof LazyFont) { + tf = ((LazyFont)tf).getRealFont(); + if (tf instanceof SingleByteFont + && ((SingleByteFont) tf).getEncoding().getName().equals("custom")) { + defineEncoding(gen, ((SingleByteFont) tf).getEncoding()); + } + } + } defineWinAnsiEncoding(gen); } gen.commentln("%FOPBeginFontReencode"); -- cgit v1.2.3 From 3a1c043a42a08f75f8fc3a311bed31460af16722 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Fri, 13 Dec 2013 20:23:54 +0000 Subject: FOP-2263: PDFDocumentGraphics2D.closePage() should not nullify currentStream; change suggested by Max Gilead git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1550834 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java | 1 - 1 file changed, 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java index e91e2231c..cb2b58c40 100644 --- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java @@ -278,7 +278,6 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D { PDFStream pdfStream = this.pdfDoc.getFactory().makeStream( PDFFilterList.CONTENT_FILTER, false); pdfStream.add(getString()); - currentStream = null; this.pdfDoc.registerObject(pdfStream); pdfContext.getCurrentPage().setContents(pdfStream); PDFAnnotList annots = pdfContext.getCurrentPage().getAnnotations(); -- cgit v1.2.3 From 6f02e19db3d538e15486180ae3b345bbfe6ecf41 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 17 Dec 2013 17:29:09 +0000 Subject: Smoke test: introduce Checkstyle violation and see if Gump will complain git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1551638 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index 86861a60b..dbd2a1c1c 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -18,7 +18,7 @@ /* $Id$ */ package org.apache.fop; - + /** * This class is used to evaluate the version information contained in the Manifest of FOP's JAR. * Note that this class can only find the version information if it's in the org.apache.fop package -- cgit v1.2.3 From 72fe70000ee1987c592f25dcd1930ec5b23320ef Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Sat, 21 Dec 2013 22:36:08 +0000 Subject: Fix checkstyle warning. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1552956 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index dbd2a1c1c..86861a60b 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -18,7 +18,7 @@ /* $Id$ */ package org.apache.fop; - + /** * This class is used to evaluate the version information contained in the Manifest of FOP's JAR. * Note that this class can only find the version information if it's in the org.apache.fop package -- cgit v1.2.3 From e7d05ec67b2652cd6ede79067d42eae8c057c955 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 7 Jan 2014 09:51:25 +0000 Subject: The length of $HeadURL$ cannot be controlled. Surround with CSOFF/CSON comments git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1556164 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index 86861a60b..a8903c74e 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -40,8 +40,9 @@ public final class Version { } if (version == null) { //Fallback if FOP is used in a development environment - String headURL - = "$HeadURL$"; + // CSOFF: LineLength + String headURL = "$HeadURL$"; + // CSON: LineLength version = headURL; final String pathPrefix = "/xmlgraphics/fop/"; int pos = version.indexOf(pathPrefix); -- cgit v1.2.3 From f788f5d12b85ddecf7daa95f4eb735139080f399 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Fri, 17 Jan 2014 23:35:00 +0000 Subject: FOP-1801: conversion B&W GIF=>PDF creates PDF with colorspace RGB if FOP0.95 and Gray if FOP0.20.5; patch submitted by Thanasis Giannimaras following suggestion by Jeremias Maerki git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1559284 13f79535-47bb-0310-9956-ffa450edef68 --- lib/xmlgraphics-commons-svn-trunk.jar | Bin 631612 -> 632012 bytes .../fop/render/pdf/AbstractImageAdapter.java | 38 +++++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src/java/org/apache/fop') diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar index 34e4d2b44..11b445c09 100644 Binary files a/lib/xmlgraphics-commons-svn-trunk.jar and b/lib/xmlgraphics-commons-svn-trunk.jar differ diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index 58299c528..471efc567 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -23,6 +23,8 @@ import java.awt.color.ICC_Profile; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.Raster; +import java.io.IOException; +import java.util.Arrays; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.logging.Log; @@ -239,24 +241,40 @@ public abstract class AbstractImageAdapter implements PDFImage { + " The image may not be handled correctly." + " Base color space: " + icm.getColorSpace() + " Image: " + image.getInfo()); } - indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName())); + ByteArrayOutputStream baout = new ByteArrayOutputStream(); int c = icm.getMapSize(); int hival = c - 1; if (hival > MAX_HIVAL) { throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL); } - indexed.add(Integer.valueOf(hival)); + boolean isDeviceGray = false; int[] palette = new int[c]; icm.getRGBs(palette); - ByteArrayOutputStream baout = new ByteArrayOutputStream(); - for (int i = 0; i < c; i++) { - // TODO Probably doesn't work for non RGB based color spaces - // See log warning above - int entry = palette[i]; - baout.write((entry & 0xFF0000) >> 16); - baout.write((entry & 0xFF00) >> 8); - baout.write(entry & 0xFF); + byte[] reds = new byte[c]; + byte[] greens = new byte[c]; + byte[] blues = new byte[c]; + icm.getReds(reds); + icm.getGreens(greens); + icm.getBlues(blues); + isDeviceGray = Arrays.equals(reds, blues) && Arrays.equals(blues, greens); + if (isDeviceGray) { + indexed.add(new PDFName("DeviceGray")); + try { + baout.write(blues); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName())); + for (int i = 0; i < c; i++) { + int entry = palette[i]; + baout.write((entry & 0xFF0000) >> 16); + baout.write((entry & 0xFF00) >> 8); + baout.write(entry & 0xFF); + } } + indexed.add(hival); + indexed.add(baout.toByteArray()); dict.put("ColorSpace", indexed); -- cgit v1.2.3