diff --git a/build.gradle b/build.gradle index d9fa9a21e1..0acedde987 100644 --- a/build.gradle +++ b/build.gradle @@ -34,9 +34,16 @@ plugins { id 'com.dorongold.task-tree' version '4.0.1' id 'org.nosphere.apache.rat' version '0.8.1' id 'distribution' - id 'com.github.spotbugs' version '6.4.7' // 6.2.0+ requires JDK 11 + + // 6.2.0+ requires JDK 11 + // this is the version of the Gradle plugin, it usually includes a + // different version of spotbugs itself + // Latest spotbugs: https://github.com/spotbugs/spotbugs + // Latest gradle plugin: https://plugins.gradle.org/plugin/com.github.spotbugs + id 'com.github.spotbugs' version '6.4.8' + id 'de.thetaphi.forbiddenapis' version '3.10' - id 'org.sonarqube' version '7.1.0.6387' + id 'org.sonarqube' version '7.2.2.6593' id 'org.cyclonedx.bom' version '2.4.1' id 'com.adarshr.test-logger' version '4.0.0' } @@ -108,13 +115,13 @@ subprojects { ext { bouncyCastleVersion = '1.83' - commonsCodecVersion = '1.20.0' + commonsCodecVersion = '1.21.0' commonsCompressVersion = '1.28.0' commonsIoVersion = '2.21.0' commonsMathVersion = '3.6.1' junitVersion = '5.13.4' - log4jVersion = '2.24.3' - mockitoVersion = '5.20.0' + log4jVersion = '2.25.3' + mockitoVersion = '5.21.0' hamcrestVersion = '3.0' xmlbeansVersion = '5.3.0' batikVersion = '1.19' @@ -210,7 +217,7 @@ subprojects { links 'https://poi.apache.org/apidocs/dev/' if (jdkVersion >= 23) links 'https://docs.oracle.com/en/java/javase/23/docs/api/' else links 'https://docs.oracle.com/en/java/javase/11/docs/api/' links 'https://xmlbeans.apache.org/docs/5.0.0/' - links 'https://commons.apache.org/proper/commons-compress/apidocs/' + links 'https://javadoc.io/doc/org.apache.commons/commons-compress/1.27.1/' use = true splitIndex = true source = "11" @@ -227,14 +234,14 @@ subprojects { tasks.withType(Jar).configureEach { duplicatesStrategy = DuplicatesStrategy.FAIL - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") doLast { // make sure we do not have distribution jar-files with different versions // in the build-dir as those lead to strange errors about "duplicate modules" // when building java9 JPMS class files ("java9") ant.delete(failOnError: true, verbose: true) { - fileset(dir: "../build/dist/maven/${project.archivesBaseName}", erroronmissingdir: false) { + fileset(dir: "../build/dist/maven/${base.archivesName.get()}", erroronmissingdir: false) { include(name: '*.jar') exclude(name: "*${version}.jar") exclude(name: "*${version}-sources.jar") @@ -260,7 +267,7 @@ subprojects { } // use failOnError=false for -javadoc and -tests as not all modules create this directory ant.delete(failOnError: false, verbose: true) { - fileset(dir: "../build/dist/maven/${project.archivesBaseName}-javadoc", erroronmissingdir: false) { + fileset(dir: "../build/dist/maven/${base.archivesName.get()}-javadoc", erroronmissingdir: false) { include(name: '*-javadoc.jar') exclude(name: "*${version}-javadoc.jar") @@ -275,7 +282,7 @@ subprojects { } } ant.delete(failOnError: false, verbose: true) { - fileset(dir: "../build/dist/maven/${project.archivesBaseName}-tests", erroronmissingdir: false) { + fileset(dir: "../build/dist/maven/${base.archivesName.get()}-tests", erroronmissingdir: false) { include(name: '*-tests.jar') exclude(name: "*${version}-tests.jar") @@ -290,8 +297,8 @@ subprojects { } } - ant.checksum(file: it.archivePath, algorithm: 'SHA-256', fileext: '.sha256', format: 'MD5SUM') - ant.checksum(file: it.archivePath, algorithm: 'SHA-512', fileext: '.sha512', format: 'MD5SUM') + ant.checksum(file: it.archiveFile.get().asFile, algorithm: 'SHA-256', fileext: '.sha256', format: 'MD5SUM') + ant.checksum(file: it.archiveFile.get().asFile, algorithm: 'SHA-512', fileext: '.sha512', format: 'MD5SUM') } } @@ -319,11 +326,11 @@ subprojects { javadocJar { // if javadocs and binaries are in the same directory, JPMS complaints about duplicated modules // in the module-path - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-javadoc") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-javadoc") } sourcesJar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") exclude 'META-INF/services/**' } @@ -503,8 +510,8 @@ subprojects { publishing { publications { POI(MavenPublication) { - groupId 'org.apache.poi' - artifactId project.archivesBaseName + groupId = 'org.apache.poi' + artifactId = base.archivesName.get() from components.java @@ -585,7 +592,7 @@ subprojects { } } - generatePomFileForPOIPublication.destination = "../build/dist/maven/${project.archivesBaseName}/${project.archivesBaseName}-${project.version}.pom" + generatePomFileForPOIPublication.destination = "../build/dist/maven/${base.archivesName.get()}/${base.archivesName.get()}-${project.version}.pom" tasks.withType(GenerateModuleMetadata).configureEach { enabled = false diff --git a/build.xml b/build.xml index 4ee14ce576..218ff899ba 100644 --- a/build.xml +++ b/build.xml @@ -268,7 +268,7 @@ under the License. - + @@ -288,7 +288,7 @@ under the License. - + diff --git a/gradlew.bat b/gradlew.bat index 5eed7ee845..db3a6ac207 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/legal/NOTICE b/legal/NOTICE index d600ff75df..cdea3ced2f 100644 --- a/legal/NOTICE +++ b/legal/NOTICE @@ -1,5 +1,5 @@ Apache POI -Copyright 2003-2025 The Apache Software Foundation +Copyright 2003-2026 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). diff --git a/osgi/pom.xml b/osgi/pom.xml index 99899b7b71..e4196dcc2e 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -231,7 +231,7 @@ org.apache.logging.log4j log4j-core - 2.24.3 + 2.25.3 test diff --git a/poi-examples/build.gradle b/poi-examples/build.gradle index 86b5a4a235..c8a5a0cca1 100644 --- a/poi-examples/build.gradle +++ b/poi-examples/build.gradle @@ -67,7 +67,7 @@ tasks.register('compileJava9', JavaCompile) { jar { dependsOn compileJava9 - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes('Automatic-Module-Name': MODULE_NAME, 'Multi-Release': 'true') diff --git a/poi-examples/src/main/java/org/apache/poi/examples/xslf/AddVideoToPptx.java.txt b/poi-examples/src/main/java/org/apache/poi/examples/xslf/AddVideoToPptx.java.txt index 0b6225b6ee..cf1ac77d24 100644 --- a/poi-examples/src/main/java/org/apache/poi/examples/xslf/AddVideoToPptx.java.txt +++ b/poi-examples/src/main/java/org/apache/poi/examples/xslf/AddVideoToPptx.java.txt @@ -1,251 +1,251 @@ -/* - * ==================================================================== - * 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.poi.xslf.usermodel; - -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.text.DecimalFormat; - -import javax.imageio.ImageIO; -import javax.xml.namespace.QName; - -import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackagePartName; -import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.openxml4j.opc.PackagingURIHelper; -import org.apache.poi.openxml4j.opc.TargetMode; -import org.apache.poi.sl.usermodel.PictureData.PictureType; -import org.apache.xmlbeans.XmlCursor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; -import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; -import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; -import org.openxmlformats.schemas.presentationml.x2006.main.CTExtension; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; -import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonMediaNodeData; -import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonTimeNodeData; -import org.openxmlformats.schemas.presentationml.x2006.main.CTTimeNodeList; -import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeIndefinite; -import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeFillType; -import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeRestartType; -import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeType; - -import com.xuggle.mediatool.IMediaReader; -import com.xuggle.mediatool.MediaListenerAdapter; -import com.xuggle.mediatool.ToolFactory; -import com.xuggle.mediatool.event.IVideoPictureEvent; -import com.xuggle.xuggler.Global; -import com.xuggle.xuggler.IContainer; -import com.xuggle.xuggler.io.InputOutputStreamHandler; - -/** - * Adding multiple videos to a slide - * - * need the Xuggler 5.4 jars: - * <repositories> - * <repository> - * <id>xuggle repo</id> - * <url>http://xuggle.googlecode.com/svn/trunk/repo/share/java/</url> - * </repository> - * </repositories> - * ... - * <dependency> - * <groupId>xuggle</groupId> - * <artifactId>xuggle-xuggler</artifactId> - * <version>5.4</version> - * </dependency> - * - * @see Apache POI XSLF Adding movie to the slide - * @see Question about embedded video in PPTX files - */ -public class AddVideoToPptx { - static DecimalFormat df_time = new DecimalFormat("0.####"); - - public static void main(String[] args) throws Exception { - URL video = new URL("http://archive.org/download/test-mpeg/test-mpeg.mpg"); - // URL video = new URL("file:test-mpeg.mpg"); - - XMLSlideShow pptx = new XMLSlideShow(); - - // add video file - String videoFileName = video.getPath().substring(video.getPath().lastIndexOf('/')+1); - PackagePartName partName = PackagingURIHelper.createPartName("/ppt/media/"+videoFileName); - PackagePart part = pptx.getPackage().createPart(partName, "video/mpeg"); - OutputStream partOs = part.getOutputStream(); - InputStream fis = video.openStream(); - byte buf[] = new byte[1024]; - for (int readBytes; (readBytes = fis.read(buf)) != -1; partOs.write(buf, 0, readBytes)); - fis.close(); - partOs.close(); - - XSLFSlide slide1 = pptx.createSlide(); - XSLFPictureShape pv1 = addPreview(pptx, slide1, part, 5, 50, 50); - addVideo(pptx, slide1, part, pv1, 5); - addTimingInfo(slide1, pv1); - XSLFPictureShape pv2 = addPreview(pptx, slide1, part, 9, 50, 250); - addVideo(pptx, slide1, part, pv2, 9); - addTimingInfo(slide1, pv2); - - FileOutputStream fos = new FileOutputStream("pptx-with-video.pptx"); - pptx.write(fos); - fos.close(); - - pptx.close(); - } - - static XSLFPictureShape addPreview(XMLSlideShow pptx, XSLFSlide slide1, PackagePart videoPart, double seconds, int x, int y) throws IOException { - // get preview after 5 sec. - IContainer ic = IContainer.make(); - InputOutputStreamHandler iosh = new InputOutputStreamHandler(videoPart.getInputStream()); - if (ic.open(iosh, IContainer.Type.READ, null) < 0) return null; - - IMediaReader mediaReader = ToolFactory.makeReader(ic); - - // stipulate that we want BufferedImages created in BGR 24bit color space - mediaReader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR); - - ImageSnapListener isl = new ImageSnapListener(seconds); - mediaReader.addListener(isl); - - // read out the contents of the media file and - // dispatch events to the attached listener - while (!isl.hasFired && mediaReader.readPacket() == null) ; - - mediaReader.close(); - ic.close(); - - // add snapshot - BufferedImage image1 = isl.image; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ImageIO.write(image1, "jpeg", bos); - XSLFPictureData snap = pptx.addPicture(bos.toByteArray(), PictureType.JPEG); - XSLFPictureShape pic1 = slide1.createPicture(snap); - pic1.setAnchor(new Rectangle(x, y, image1.getWidth(), image1.getHeight())); - return pic1; - } - - static void addVideo(XMLSlideShow pptx, XSLFSlide slide1, PackagePart videoPart, XSLFPictureShape pic1, double seconds) throws IOException { - - // add video shape - PackagePartName partName = videoPart.getPartName(); - PackageRelationship prsEmbed1 = slide1.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, "http://schemas.microsoft.com/office/2007/relationships/media"); - PackageRelationship prsExec1 = slide1.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video"); - CTPicture xpic1 = (CTPicture)pic1.getXmlObject(); - CTHyperlink link1 = xpic1.getNvPicPr().getCNvPr().addNewHlinkClick(); - link1.setId(""); - link1.setAction("ppaction://media"); - - // add video relation - CTApplicationNonVisualDrawingProps nvPr = xpic1.getNvPicPr().getNvPr(); - nvPr.addNewVideoFile().setLink(prsExec1.getId()); - CTExtension ext = nvPr.addNewExtLst().addNewExt(); - // see http://msdn.microsoft.com/en-us/library/dd950140(v=office.12).aspx - ext.setUri("{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}"); - String p14Ns = "http://schemas.microsoft.com/office/powerpoint/2010/main"; - - try (XmlCursor cur = ext.newCursor()) { - cur.toEndToken(); - cur.beginElement(new QName(p14Ns, "media", "p14")); - cur.insertNamespace("p14", p14Ns); - cur.insertAttributeWithValue(new QName(STRelationshipId.type.getName().getNamespaceURI(), "embed"), prsEmbed1.getId()); - cur.beginElement(new QName(p14Ns, "trim", "p14")); - cur.insertAttributeWithValue("st", df_time.format(seconds*1000.0)); - } - } - - static void addTimingInfo(XSLFSlide slide1, XSLFPictureShape pic1) { - // add slide timing information, so video can be controlled - CTSlide xslide = slide1.getXmlObject(); - CTTimeNodeList ctnl; - if (!xslide.isSetTiming()) { - CTTLCommonTimeNodeData ctn = xslide.addNewTiming().addNewTnLst().addNewPar().addNewCTn(); - ctn.setDur(STTLTimeIndefinite.INDEFINITE); - ctn.setRestart(STTLTimeNodeRestartType.NEVER); - ctn.setNodeType(STTLTimeNodeType.TM_ROOT); - ctnl = ctn.addNewChildTnLst(); - } else { - ctnl = xslide.getTiming().getTnLst().getParArray(0).getCTn().getChildTnLst(); - } - - CTTLCommonMediaNodeData cmedia = ctnl.addNewVideo().addNewCMediaNode(); - cmedia.setVol(80000); - CTTLCommonTimeNodeData ctn = cmedia.addNewCTn(); - ctn.setFill(STTLTimeNodeFillType.HOLD); - ctn.setDisplay(false); - ctn.addNewStCondLst().addNewCond().setDelay(STTLTimeIndefinite.INDEFINITE); - cmedia.addNewTgtEl().addNewSpTgt().setSpid(""+pic1.getShapeId()); - } - - - static class ImageSnapListener extends MediaListenerAdapter { - final double SECONDS_BETWEEN_FRAMES; - final long MICRO_SECONDS_BETWEEN_FRAMES; - boolean hasFired = false; - BufferedImage image = null; - - // The video stream index, used to ensure we display frames from one and - // only one video stream from the media container. - int mVideoStreamIndex = -1; - - // Time of last frame write - long mLastPtsWrite = Global.NO_PTS; - - public ImageSnapListener(double seconds) { - SECONDS_BETWEEN_FRAMES = seconds; - MICRO_SECONDS_BETWEEN_FRAMES = - (long)(Global.DEFAULT_PTS_PER_SECOND * SECONDS_BETWEEN_FRAMES); - } - - - @Override - public void onVideoPicture(IVideoPictureEvent event) { - - if (event.getStreamIndex() != mVideoStreamIndex) { - // if the selected video stream id is not yet set, go ahead an - // select this lucky video stream - if (mVideoStreamIndex != -1) return; - mVideoStreamIndex = event.getStreamIndex(); - } - - long evtTS = event.getTimeStamp(); - - // if uninitialized, back date mLastPtsWrite to get the very first frame - if (mLastPtsWrite == Global.NO_PTS) - mLastPtsWrite = Math.max(0, evtTS - MICRO_SECONDS_BETWEEN_FRAMES); - - // if its time to write the next frame - if (evtTS - mLastPtsWrite >= MICRO_SECONDS_BETWEEN_FRAMES) { - if (!hasFired) { - image = event.getImage(); - hasFired = true; - } - // update last write time - mLastPtsWrite += MICRO_SECONDS_BETWEEN_FRAMES; - } - } - } - -} +/* + * ==================================================================== + * 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.poi.xslf.usermodel; + +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.text.DecimalFormat; + +import javax.imageio.ImageIO; +import javax.xml.namespace.QName; + +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.TargetMode; +import org.apache.poi.sl.usermodel.PictureData.PictureType; +import org.apache.xmlbeans.XmlCursor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; +import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; +import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; +import org.openxmlformats.schemas.presentationml.x2006.main.CTExtension; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; +import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonMediaNodeData; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonTimeNodeData; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTimeNodeList; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeIndefinite; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeFillType; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeRestartType; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeType; + +import com.xuggle.mediatool.IMediaReader; +import com.xuggle.mediatool.MediaListenerAdapter; +import com.xuggle.mediatool.ToolFactory; +import com.xuggle.mediatool.event.IVideoPictureEvent; +import com.xuggle.xuggler.Global; +import com.xuggle.xuggler.IContainer; +import com.xuggle.xuggler.io.InputOutputStreamHandler; + +/** + * Adding multiple videos to a slide + * + * need the Xuggler 5.4 jars: + * <repositories> + * <repository> + * <id>xuggle repo</id> + * <url>http://xuggle.googlecode.com/svn/trunk/repo/share/java/</url> + * </repository> + * </repositories> + * ... + * <dependency> + * <groupId>xuggle</groupId> + * <artifactId>xuggle-xuggler</artifactId> + * <version>5.4</version> + * </dependency> + * + * @see Apache POI XSLF Adding movie to the slide + * @see Question about embedded video in PPTX files + */ +public class AddVideoToPptx { + static DecimalFormat df_time = new DecimalFormat("0.####"); + + public static void main(String[] args) throws Exception { + URL video = new URL("http://archive.org/download/test-mpeg/test-mpeg.mpg"); + // URL video = new URL("file:test-mpeg.mpg"); + + XMLSlideShow pptx = new XMLSlideShow(); + + // add video file + String videoFileName = video.getPath().substring(video.getPath().lastIndexOf('/')+1); + PackagePartName partName = PackagingURIHelper.createPartName("/ppt/media/"+videoFileName); + PackagePart part = pptx.getPackage().createPart(partName, "video/mpeg"); + OutputStream partOs = part.getOutputStream(); + InputStream fis = video.openStream(); + byte buf[] = new byte[1024]; + for (int readBytes; (readBytes = fis.read(buf)) != -1; partOs.write(buf, 0, readBytes)); + fis.close(); + partOs.close(); + + XSLFSlide slide1 = pptx.createSlide(); + XSLFPictureShape pv1 = addPreview(pptx, slide1, part, 5, 50, 50); + addVideo(pptx, slide1, part, pv1, 5); + addTimingInfo(slide1, pv1); + XSLFPictureShape pv2 = addPreview(pptx, slide1, part, 9, 50, 250); + addVideo(pptx, slide1, part, pv2, 9); + addTimingInfo(slide1, pv2); + + FileOutputStream fos = new FileOutputStream("pptx-with-video.pptx"); + pptx.write(fos); + fos.close(); + + pptx.close(); + } + + static XSLFPictureShape addPreview(XMLSlideShow pptx, XSLFSlide slide1, PackagePart videoPart, double seconds, int x, int y) throws IOException { + // get preview after 5 sec. + IContainer ic = IContainer.make(); + InputOutputStreamHandler iosh = new InputOutputStreamHandler(videoPart.getInputStream()); + if (ic.open(iosh, IContainer.Type.READ, null) < 0) return null; + + IMediaReader mediaReader = ToolFactory.makeReader(ic); + + // stipulate that we want BufferedImages created in BGR 24bit color space + mediaReader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR); + + ImageSnapListener isl = new ImageSnapListener(seconds); + mediaReader.addListener(isl); + + // read out the contents of the media file and + // dispatch events to the attached listener + while (!isl.hasFired && mediaReader.readPacket() == null) ; + + mediaReader.close(); + ic.close(); + + // add snapshot + BufferedImage image1 = isl.image; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIO.write(image1, "jpeg", bos); + XSLFPictureData snap = pptx.addPicture(bos.toByteArray(), PictureType.JPEG); + XSLFPictureShape pic1 = slide1.createPicture(snap); + pic1.setAnchor(new Rectangle(x, y, image1.getWidth(), image1.getHeight())); + return pic1; + } + + static void addVideo(XMLSlideShow pptx, XSLFSlide slide1, PackagePart videoPart, XSLFPictureShape pic1, double seconds) throws IOException { + + // add video shape + PackagePartName partName = videoPart.getPartName(); + PackageRelationship prsEmbed1 = slide1.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, "http://schemas.microsoft.com/office/2007/relationships/media"); + PackageRelationship prsExec1 = slide1.getPackagePart().addRelationship(partName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video"); + CTPicture xpic1 = (CTPicture)pic1.getXmlObject(); + CTHyperlink link1 = xpic1.getNvPicPr().getCNvPr().addNewHlinkClick(); + link1.setId(""); + link1.setAction("ppaction://media"); + + // add video relation + CTApplicationNonVisualDrawingProps nvPr = xpic1.getNvPicPr().getNvPr(); + nvPr.addNewVideoFile().setLink(prsExec1.getId()); + CTExtension ext = nvPr.addNewExtLst().addNewExt(); + // see http://msdn.microsoft.com/en-us/library/dd950140(v=office.12).aspx + ext.setUri("{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}"); + String p14Ns = "http://schemas.microsoft.com/office/powerpoint/2010/main"; + + try (XmlCursor cur = ext.newCursor()) { + cur.toEndToken(); + cur.beginElement(new QName(p14Ns, "media", "p14")); + cur.insertNamespace("p14", p14Ns); + cur.insertAttributeWithValue(new QName(STRelationshipId.type.getName().getNamespaceURI(), "embed"), prsEmbed1.getId()); + cur.beginElement(new QName(p14Ns, "trim", "p14")); + cur.insertAttributeWithValue("st", df_time.format(seconds*1000.0)); + } + } + + static void addTimingInfo(XSLFSlide slide1, XSLFPictureShape pic1) { + // add slide timing information, so video can be controlled + CTSlide xslide = slide1.getXmlObject(); + CTTimeNodeList ctnl; + if (!xslide.isSetTiming()) { + CTTLCommonTimeNodeData ctn = xslide.addNewTiming().addNewTnLst().addNewPar().addNewCTn(); + ctn.setDur(STTLTimeIndefinite.INDEFINITE); + ctn.setRestart(STTLTimeNodeRestartType.NEVER); + ctn.setNodeType(STTLTimeNodeType.TM_ROOT); + ctnl = ctn.addNewChildTnLst(); + } else { + ctnl = xslide.getTiming().getTnLst().getParArray(0).getCTn().getChildTnLst(); + } + + CTTLCommonMediaNodeData cmedia = ctnl.addNewVideo().addNewCMediaNode(); + cmedia.setVol(80000); + CTTLCommonTimeNodeData ctn = cmedia.addNewCTn(); + ctn.setFill(STTLTimeNodeFillType.HOLD); + ctn.setDisplay(false); + ctn.addNewStCondLst().addNewCond().setDelay(STTLTimeIndefinite.INDEFINITE); + cmedia.addNewTgtEl().addNewSpTgt().setSpid(""+pic1.getShapeId()); + } + + + static class ImageSnapListener extends MediaListenerAdapter { + final double SECONDS_BETWEEN_FRAMES; + final long MICRO_SECONDS_BETWEEN_FRAMES; + boolean hasFired = false; + BufferedImage image = null; + + // The video stream index, used to ensure we display frames from one and + // only one video stream from the media container. + int mVideoStreamIndex = -1; + + // Time of last frame write + long mLastPtsWrite = Global.NO_PTS; + + public ImageSnapListener(double seconds) { + SECONDS_BETWEEN_FRAMES = seconds; + MICRO_SECONDS_BETWEEN_FRAMES = + (long)(Global.DEFAULT_PTS_PER_SECOND * SECONDS_BETWEEN_FRAMES); + } + + + @Override + public void onVideoPicture(IVideoPictureEvent event) { + + if (event.getStreamIndex() != mVideoStreamIndex) { + // if the selected video stream id is not yet set, go ahead an + // select this lucky video stream + if (mVideoStreamIndex != -1) return; + mVideoStreamIndex = event.getStreamIndex(); + } + + long evtTS = event.getTimeStamp(); + + // if uninitialized, back date mLastPtsWrite to get the very first frame + if (mLastPtsWrite == Global.NO_PTS) + mLastPtsWrite = Math.max(0, evtTS - MICRO_SECONDS_BETWEEN_FRAMES); + + // if its time to write the next frame + if (evtTS - mLastPtsWrite >= MICRO_SECONDS_BETWEEN_FRAMES) { + if (!hasFired) { + image = event.getImage(); + hasFired = true; + } + // update last write time + mLastPtsWrite += MICRO_SECONDS_BETWEEN_FRAMES; + } + } + } + +} diff --git a/poi-examples/src/main/java/org/apache/poi/examples/xslf/bar-chart-data.txt b/poi-examples/src/main/java/org/apache/poi/examples/xslf/bar-chart-data.txt index 22c7b86ab8..5f7d8e1ca7 100644 --- a/poi-examples/src/main/java/org/apache/poi/examples/xslf/bar-chart-data.txt +++ b/poi-examples/src/main/java/org/apache/poi/examples/xslf/bar-chart-data.txt @@ -1,12 +1,12 @@ -10 languages with most speakers as first language -countries,speakers,language -58,315,العربية -4,243,বাংলা -38,1299,中文 -118,378,English -4,260,हिन्दी -2,128,日本語 -15,223,português -6,119,ਪੰਜਾਬੀ -18,154,Русский язык -31,442,español +10 languages with most speakers as first language +countries,speakers,language +58,315,العربية +4,243,বাংলা +38,1299,中文 +118,378,English +4,260,हिन्दी +2,128,日本語 +15,223,português +6,119,ਪੰਜਾਬੀ +18,154,Русский язык +31,442,español diff --git a/poi-examples/src/main/java/org/apache/poi/examples/xslf/pie-chart-data.txt b/poi-examples/src/main/java/org/apache/poi/examples/xslf/pie-chart-data.txt index 40b6959c2c..3920f037e8 100644 --- a/poi-examples/src/main/java/org/apache/poi/examples/xslf/pie-chart-data.txt +++ b/poi-examples/src/main/java/org/apache/poi/examples/xslf/pie-chart-data.txt @@ -1,4 +1,4 @@ -My Chart -First 1.0 -Second 3.0 +My Chart +First 1.0 +Second 3.0 Third 4.0 \ No newline at end of file diff --git a/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/bar-chart-data.txt b/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/bar-chart-data.txt index 22c7b86ab8..5f7d8e1ca7 100644 --- a/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/bar-chart-data.txt +++ b/poi-examples/src/main/java/org/apache/poi/examples/xwpf/usermodel/bar-chart-data.txt @@ -1,12 +1,12 @@ -10 languages with most speakers as first language -countries,speakers,language -58,315,العربية -4,243,বাংলা -38,1299,中文 -118,378,English -4,260,हिन्दी -2,128,日本語 -15,223,português -6,119,ਪੰਜਾਬੀ -18,154,Русский язык -31,442,español +10 languages with most speakers as first language +countries,speakers,language +58,315,العربية +4,243,বাংলা +38,1299,中文 +118,378,English +4,260,हिन्दी +2,128,日本語 +15,223,português +6,119,ਪੰਜਾਬੀ +18,154,Русский язык +31,442,español diff --git a/poi-excelant/build.gradle b/poi-excelant/build.gradle index 4e0e2acabd..290125cebc 100644 --- a/poi-excelant/build.gradle +++ b/poi-excelant/build.gradle @@ -95,7 +95,7 @@ tasks.register('compileTest9', JavaCompile) { jar { dependsOn compileJava9 - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes('Automatic-Module-Name': MODULE_NAME, 'Multi-Release': 'true') @@ -119,7 +119,7 @@ sourcesJar { // Create a separate jar for test-code to depend on it in other projects // See http://stackoverflow.com/questions/5144325/gradle-test-dependency task testJar(type: Jar, dependsOn: [ testClasses, compileTest9 ] ) { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-tests") setArchiveClassifier 'tests' // ignore second module-info.class from main diff --git a/poi-integration/build.gradle b/poi-integration/build.gradle index 0834fa5b3b..3a09a4bb33 100644 --- a/poi-integration/build.gradle +++ b/poi-integration/build.gradle @@ -109,7 +109,7 @@ tasks.register('compileTest9', JavaCompile) { jar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes('Automatic-Module-Name': MODULE_NAME, 'Multi-Release': 'true') @@ -119,7 +119,7 @@ jar { // Create a separate jar for test-code to depend on it in other projects // See http://stackoverflow.com/questions/5144325/gradle-test-dependency task testJar(type: Jar, dependsOn: [ testClasses, compileTest9 ] ) { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-tests") setArchiveClassifier 'tests' // ignore second module-info.class from main @@ -156,9 +156,13 @@ test { } } -javadoc { enabled(false) } +javadoc { + enabled = false +} -sourcesJar { enabled(false) } +sourcesJar { + enabled = false +} generateMetadataFileForPOIPublication.enabled = false publishPOIPublicationToMavenLocal.enabled = false diff --git a/poi-integration/src/test/java/org/apache/poi/stress/FileHandlerKnown.java b/poi-integration/src/test/java/org/apache/poi/stress/FileHandlerKnown.java index c3f1e11137..1e113c714a 100644 --- a/poi-integration/src/test/java/org/apache/poi/stress/FileHandlerKnown.java +++ b/poi-integration/src/test/java/org/apache/poi/stress/FileHandlerKnown.java @@ -29,6 +29,8 @@ public enum FileHandlerKnown { HSLF, HSMF, HSSF, + HEMF, + HWMF, HWPF, OPC, POIFS, diff --git a/poi-integration/src/test/java/org/apache/poi/stress/HEMFFileHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/HEMFFileHandler.java new file mode 100644 index 0000000000..dd0e5fbe27 --- /dev/null +++ b/poi-integration/src/test/java/org/apache/poi/stress/HEMFFileHandler.java @@ -0,0 +1,85 @@ +/* ==================================================================== + 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.poi.stress; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.poi.hemf.record.emf.HemfRecord; +import org.apache.poi.hemf.record.emf.HemfRecordType; +import org.apache.poi.hemf.record.emf.HemfText; +import org.apache.poi.hemf.usermodel.HemfPicture; +import org.junit.jupiter.api.Test; + +public class HEMFFileHandler implements FileHandler { + + @Override + public void handleExtracting(File file) throws Exception { + try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { + HemfPicture picture = new HemfPicture(stream); + + // mimic a bit what e.g. Tika does to extract some information from .emf files + for (HemfRecord record : picture.getRecords()) { + if (record.getEmfRecordType().equals(HemfRecordType.extTextOutW)) { + assertInstanceOf(HemfText.EmfExtTextOutW.class, record); + HemfText.EmfExtTextOutW textOut = (HemfText.EmfExtTextOutW) record; + textOut.getText(StandardCharsets.UTF_16LE); + } else if (record.getEmfRecordType().equals(HemfRecordType.extTextOutA)) { + assertInstanceOf(HemfText.EmfExtTextOutA.class, record); + HemfText.EmfExtTextOutA textOut = (HemfText.EmfExtTextOutA) record; + textOut.getText(StandardCharsets.UTF_8); + } + } + } + } + + @Override + public void handleAdditional(File file) throws Exception { + // no additional checks for now + } + + @Override + public void handleFile(InputStream stream, String path) throws Exception { + HemfPicture picture = new HemfPicture(stream); + + for (HemfRecord record : picture.getRecords()) { + record.getEmfRecordType(); + record.getGenericRecordType(); + } + + BufferedImage dest = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); + picture.draw(dest.createGraphics(), new Rectangle2D.Double(0, 0, 256, 256)); + } + + @Test + void test() throws Exception { + String file = "test-data/slideshow/wrench.emf"; + + try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { + handleFile(stream, file); + } + + handleExtracting(new File(file)); + } +} diff --git a/poi-integration/src/test/java/org/apache/poi/stress/HWMFFileHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/HWMFFileHandler.java new file mode 100644 index 0000000000..3429844bfb --- /dev/null +++ b/poi-integration/src/test/java/org/apache/poi/stress/HWMFFileHandler.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. +==================================================================== */ +package org.apache.poi.stress; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.nio.charset.Charset; + +import org.apache.poi.hwmf.record.HwmfFont; +import org.apache.poi.hwmf.record.HwmfRecord; +import org.apache.poi.hwmf.record.HwmfRecordType; +import org.apache.poi.hwmf.record.HwmfText; +import org.apache.poi.hwmf.usermodel.HwmfPicture; +import org.apache.poi.util.LocaleUtil; +import org.junit.jupiter.api.Test; + +public class HWMFFileHandler implements FileHandler { + + @Override + public void handleExtracting(File file) throws Exception { + try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { + HwmfPicture picture = new HwmfPicture(stream); + Charset charset = LocaleUtil.CHARSET_1252; + + // mimic a bit what e.g. Tika does to extract some information from .wmf files + for (HwmfRecord record : picture.getRecords()) { + if (record.getWmfRecordType().equals(HwmfRecordType.createFontIndirect)) { + HwmfFont font = ((HwmfText.WmfCreateFontIndirect) record).getFont(); + charset = (font.getCharset() == null || font.getCharset().getCharset() == null) ? + LocaleUtil.CHARSET_1252 : font.getCharset().getCharset(); + } + + if (record.getWmfRecordType().equals(HwmfRecordType.extTextOut)) { + assertInstanceOf(HwmfText.WmfExtTextOut.class, record); + HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut) record; + textOut.getText(charset); + } else if (record.getWmfRecordType().equals(HwmfRecordType.textOut)) { + assertInstanceOf(HwmfText.WmfTextOut.class, record); + HwmfText.WmfTextOut textOut = (HwmfText.WmfTextOut) record; + textOut.getText(charset); + } + } + } + } + + @Override + public void handleAdditional(File file) throws Exception { + // no additional checks for now + } + + @Override + public void handleFile(InputStream stream, String path) throws Exception { + HwmfPicture picture = new HwmfPicture(stream); + + for (HwmfRecord record : picture.getRecords()) { + record.getWmfRecordType(); + record.getGenericRecordType(); + } + + BufferedImage dest = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); + picture.draw(dest.createGraphics()); + } + + @Test + void test() throws Exception { + String file = "test-data/slideshow/santa.wmf"; + + try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { + handleFile(stream, file); + } + + handleExtracting(new File(file)); + } +} diff --git a/poi-integration/src/test/java/org/apache/poi/stress/SlideShowHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/SlideShowHandler.java index 305793cd1e..9166094540 100644 --- a/poi-integration/src/test/java/org/apache/poi/stress/SlideShowHandler.java +++ b/poi-integration/src/test/java/org/apache/poi/stress/SlideShowHandler.java @@ -29,6 +29,7 @@ import java.lang.ref.WeakReference; import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; import org.apache.poi.sl.draw.Drawable; import org.apache.poi.sl.usermodel.GroupShape; +import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.Notes; import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.Shape; @@ -76,8 +77,11 @@ public abstract class SlideShowHandler extends POIFSFileHandler { } } - for (Shape shape : s.getMasterSheet()) { - readShapes(shape); + MasterSheet masterSheet = s.getMasterSheet(); + if (masterSheet != null) { + for (Shape shape : masterSheet) { + readShapes(shape); + } } } } diff --git a/poi-integration/src/test/java/org/apache/poi/stress/SpreadsheetHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/SpreadsheetHandler.java index 1e57bb856a..40f103b523 100644 --- a/poi-integration/src/test/java/org/apache/poi/stress/SpreadsheetHandler.java +++ b/poi-integration/src/test/java/org/apache/poi/stress/SpreadsheetHandler.java @@ -37,6 +37,8 @@ public abstract class SpreadsheetHandler extends AbstractFileHandler { // try to access some of the content readContent(wb); + extractEmbedded(wb); + // write out the file writeToArray(wb); diff --git a/poi-integration/src/test/java/org/apache/poi/stress/TestAllFiles.java b/poi-integration/src/test/java/org/apache/poi/stress/TestAllFiles.java index b73914f09e..a1d85e5f1d 100644 --- a/poi-integration/src/test/java/org/apache/poi/stress/TestAllFiles.java +++ b/poi-integration/src/test/java/org/apache/poi/stress/TestAllFiles.java @@ -144,8 +144,14 @@ public class TestAllFiles { "document/clusterfuzz-testcase-POIHWPFFuzzer-5696094627495936.doc", "spreadsheet/clusterfuzz-testcase-minimized-POIHSSFFuzzer-4657005060816896.xls", "diagram/clusterfuzz-testcase-minimized-POIHDGFFuzzer-4913778037489664.vsd", - "diagram/clusterfuzz-testcase-minimized-POIHDGFFuzzer-6478389109981184.vsd" - }); + "diagram/clusterfuzz-testcase-minimized-POIHDGFFuzzer-6478389109981184.vsd", + "publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-4918886059278336.pub", + "publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-4918886059278336.pub", + "publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-6325615354773504.pub", + "spreadsheet/clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", + "spreadsheet/rde.imf.ru_sites_default_files_rde_documents_vodootvedenie_2020.xlsb.xls", + "publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-6325615354773504.pub", +}); private static final Set EXPECTED_FAILURES = StressTestUtils.unmodifiableHashSet( "document/truncated62886.docx", diff --git a/poi-integration/src/test/java/org/apache/poi/stress/XSSFBFileHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/XSSFBFileHandler.java index fb130b9d72..862e89be33 100644 --- a/poi-integration/src/test/java/org/apache/poi/stress/XSSFBFileHandler.java +++ b/poi-integration/src/test/java/org/apache/poi/stress/XSSFBFileHandler.java @@ -78,7 +78,7 @@ public class XSSFBFileHandler extends AbstractFileHandler { XSSFBEventBasedExcelExtractor ex = new XSSFBEventBasedExcelExtractor(pkg); String txt = ex.getText(); if (txt.length() < 1) { - throw new RuntimeException("Should have gotten some text."); + throw new IllegalArgumentException("Should have gotten some text."); } } diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsd index 7423c85de0..112239e7b9 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsd @@ -1,39 +1,39 @@ - - - - - - - - - - - - A base64-encoded value that specifies the encrypted form of the intermediate key, which is encrypted with the public key contained within the X509Certificate attribute. - - - A base64-encoded value that specifies a DER-encoded X.509 certificate (1) used to encrypt the intermediate key. The certificate (1) MUST contain only the public portion of the public-private key pair. - - - A base64-encoded value that specifies the HMAC of the binary data obtained by base64-decoding the X509Certificate attribute. The hashing algorithm used to derive the HMAC MUST be the hashing algorithm specified for the Encryption.keyData element. The secret key used to derive the HMAC MUST be the intermediate key. If the intermediate key is reset, any CertificateKeyEncryptor elements are also reset to contain the new intermediate key, except that the certVerifier attribute MUST match the value calculated using the current intermediate key, to verify that the CertificateKeyEncryptor element actually encrypted the current intermediate key. If a CertificateKeyEncryptor element does not have a correct certVerifier attribute, it MUST be discarded. - - - - + + + + + + + + + + + + A base64-encoded value that specifies the encrypted form of the intermediate key, which is encrypted with the public key contained within the X509Certificate attribute. + + + A base64-encoded value that specifies a DER-encoded X.509 certificate (1) used to encrypt the intermediate key. The certificate (1) MUST contain only the public portion of the public-private key pair. + + + A base64-encoded value that specifies the HMAC of the binary data obtained by base64-decoding the X509Certificate attribute. The hashing algorithm used to derive the HMAC MUST be the hashing algorithm specified for the Encryption.keyData element. The secret key used to derive the HMAC MUST be the intermediate key. If the intermediate key is reset, any CertificateKeyEncryptor elements are also reset to contain the new intermediate key, except that the certVerifier attribute MUST match the value calculated using the current intermediate key, to verify that the CertificateKeyEncryptor element actually encrypted the current intermediate key. If a CertificateKeyEncryptor element does not have a correct certVerifier attribute, it MUST be discarded. + + + + diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsdconfig b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsdconfig index 73a27fa50a..b65915633e 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsdconfig +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionCertificate.xsdconfig @@ -1,24 +1,24 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsd index 5b08560c3a..d33ca773b5 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsd @@ -1,259 +1,259 @@ - - - - - - - - An unsigned integer that specifies the number of bytes used by a salt. It MUST be at least 1 and no greater than 65,536. - - - - - - - - - An unsigned integer that specifies the number of bytes used to encrypt one block of data. It MUST be at least 2, no greater than 4096, and a multiple of 2. - - - - - - - - - An unsigned integer that specifies the number of bits used by an encryption algorithm. It MUST be at least 8 and a multiple of 8. - - - - - - - - An unsigned integer that specifies the number of bytes used by a hash value. It MUST be at least 1, no greater than 65,536, and the same number of bytes as the hash algorithm emits. - - - - - - - - - An unsigned integer that specifies the number of times to iterate on a hash of a password. It MUST NOT be greater than 10,000,000. - - - - - - - - - modified for poi - list is restricted to given list in [ms-offcrypto] - A string that specifies the cipher algorithm. Values that are not defined MAY be used, and a compliant implementation is not required to support all defined values. Any algorithm that can be resolved by name by the underlying operating system can be used for hashing or encryption. Only block algorithms are supported for encryption. AES-128 is the default encryption algorithm, and SHA-1 is the default hashing algorithm if no other algorithms have been configured. - - - - - MUST conform to the AES algorithm. - - - - - MUST conform to the algorithm as specified in [RFC2268] (http://tools.ietf.org/html/rfc2268). The use of RC2 is not recommended. If RC2 is used with a key length of less than 128 bits, documents could interoperate incorrectly across different versions of Windows. - - - - - MUST NOT be used. - - - - - MUST conform to the DES algorithm. The use of DES is not recommended. If DES is used, the key length specified in the KeyBits element is required to be set to 64 for 56-bit encryption, and the key decrypted from encryptedKeyValue of KeyEncryptor is required to include the DES parity bits. - - - - - MUST conform to the algorithm as specified in [DRAFT-DESX] (http://tools.ietf.org/html/draft-ietf-ipsec-ciph-desx-00). The use of DESX is not recommended. If DESX is used, documents could interoperate incorrectly across different versions of Windows. - - - - - MUST conform to the algorithm as specified in [RFC1851] (http://tools.ietf.org/html/rfc1851). If 3DES or 3DES_112 is used, the key length specified in the KeyBits element is required to be set to 192 for 168-bit encryption and 128 for 112-bit encryption, and the key decrypted from encryptedKeyValue of KeyEncryptor is required to include the DES parity bits. - - - - - see 3DES - - - - - - - A string that specifies the chaining mode used by CipherAlgorithm. For more details about chaining modes, see [BCMO800-38A] (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf). - - - - - block chaining (CBC) - - - - - Cipher feedback chaining (CFB), with an 8-bit window - - - - - - - modified for poi - list is restricted to given list in [ms-offcrypto] - A string specifying a hashing algorithm. Values that are not defined MAY be used, and a compliant implementation is not required to support all defined values. - - - - - MUST conform to the algorithm as specified in [RFC4634] (http://tools.ietf.org/html/rfc4634). - - - - - see SHA1 - - - - - see SHA1 - - - - - see SHA1 - - - - - MUST conform to MD5. - - - - - MUST conform to the algorithm as specified in [RFC1320] (http://tools.ietf.org/html/rfc1320). - - - - - MUST conform to the algorithm as specified in [RFC1319] (http://tools.ietf.org/html/rfc1319). - - - - - MUST conform to the hash functions specified in [ISO/IEC 10118]. (https://en.wikipedia.org/wiki/RIPEMD) - - - - - see RIPEMD-128 (https://en.wikipedia.org/wiki/RIPEMD) - - - - - see RIPEMD-128 (https://en.wikipedia.org/wiki/ISO/IEC_10118-3) - - - - - - - A complex type that specifies the encryption used within this element. The saltValue attribute is a base64-encoded binary value that is randomly generated. The number of bytes required to decode the saltValue attribute MUST be equal to the value of the saltSize attribute. - - - - - - - - - - - - - A complex type that specifies data used to verify whether the encrypted data passes an integrity check. It MUST be generated using the method specified in section 2.3.4.14 (http://msdn.microsoft.com/en-us/library/dd924068(v=office.12).aspx). - - - - A base64-encoded value that specifies an encrypted key used in calculating the encryptedHmacValue. - - - - - A base64-encoded value that specifies an HMAC derived from encryptedHmacKey and the encrypted data. - - - - - - modified for POI - A complex type that specifies the parameters used to encrypt an intermediate key, which is used to perform the final encryption of the document. To ensure extensibility, arbitrary elements can be defined to encrypt the intermediate key. The intermediate key MUST be the same for all KeyEncryptor elements. - - - - - - - - modified for POI - - - - - - - - - - - - A sequence of KeyEncryptor elements. Exactly one KeyEncryptors element MUST be present, and the KeyEncryptors element MUST contain at least one KeyEncryptor. - - - - - - - - - - - modified for POI - All ECMA-376 documents [ECMA-376] encrypted by Microsoft Office using agile encryption will have a DataIntegrity element present. The schema allows for a DataIntegrity element to not be present because the encryption schema can be used by applications that do not create ECMA-376 documents [ECMA-376]. - - - - - The KeyEncryptor element, which MUST be used when encrypting password-protected agile encryption documents, is either a PasswordKeyEncryptor or a CertificateKeyEncryptor. Exactly one PasswordKeyEncryptor MUST be present. Zero or more CertificateKeyEncryptor elements are contained within the KeyEncryptors element. - - - - - - + + + + + + + + An unsigned integer that specifies the number of bytes used by a salt. It MUST be at least 1 and no greater than 65,536. + + + + + + + + + An unsigned integer that specifies the number of bytes used to encrypt one block of data. It MUST be at least 2, no greater than 4096, and a multiple of 2. + + + + + + + + + An unsigned integer that specifies the number of bits used by an encryption algorithm. It MUST be at least 8 and a multiple of 8. + + + + + + + + An unsigned integer that specifies the number of bytes used by a hash value. It MUST be at least 1, no greater than 65,536, and the same number of bytes as the hash algorithm emits. + + + + + + + + + An unsigned integer that specifies the number of times to iterate on a hash of a password. It MUST NOT be greater than 10,000,000. + + + + + + + + + modified for poi - list is restricted to given list in [ms-offcrypto] + A string that specifies the cipher algorithm. Values that are not defined MAY be used, and a compliant implementation is not required to support all defined values. Any algorithm that can be resolved by name by the underlying operating system can be used for hashing or encryption. Only block algorithms are supported for encryption. AES-128 is the default encryption algorithm, and SHA-1 is the default hashing algorithm if no other algorithms have been configured. + + + + + MUST conform to the AES algorithm. + + + + + MUST conform to the algorithm as specified in [RFC2268] (http://tools.ietf.org/html/rfc2268). The use of RC2 is not recommended. If RC2 is used with a key length of less than 128 bits, documents could interoperate incorrectly across different versions of Windows. + + + + + MUST NOT be used. + + + + + MUST conform to the DES algorithm. The use of DES is not recommended. If DES is used, the key length specified in the KeyBits element is required to be set to 64 for 56-bit encryption, and the key decrypted from encryptedKeyValue of KeyEncryptor is required to include the DES parity bits. + + + + + MUST conform to the algorithm as specified in [DRAFT-DESX] (http://tools.ietf.org/html/draft-ietf-ipsec-ciph-desx-00). The use of DESX is not recommended. If DESX is used, documents could interoperate incorrectly across different versions of Windows. + + + + + MUST conform to the algorithm as specified in [RFC1851] (http://tools.ietf.org/html/rfc1851). If 3DES or 3DES_112 is used, the key length specified in the KeyBits element is required to be set to 192 for 168-bit encryption and 128 for 112-bit encryption, and the key decrypted from encryptedKeyValue of KeyEncryptor is required to include the DES parity bits. + + + + + see 3DES + + + + + + + A string that specifies the chaining mode used by CipherAlgorithm. For more details about chaining modes, see [BCMO800-38A] (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf). + + + + + block chaining (CBC) + + + + + Cipher feedback chaining (CFB), with an 8-bit window + + + + + + + modified for poi - list is restricted to given list in [ms-offcrypto] + A string specifying a hashing algorithm. Values that are not defined MAY be used, and a compliant implementation is not required to support all defined values. + + + + + MUST conform to the algorithm as specified in [RFC4634] (http://tools.ietf.org/html/rfc4634). + + + + + see SHA1 + + + + + see SHA1 + + + + + see SHA1 + + + + + MUST conform to MD5. + + + + + MUST conform to the algorithm as specified in [RFC1320] (http://tools.ietf.org/html/rfc1320). + + + + + MUST conform to the algorithm as specified in [RFC1319] (http://tools.ietf.org/html/rfc1319). + + + + + MUST conform to the hash functions specified in [ISO/IEC 10118]. (https://en.wikipedia.org/wiki/RIPEMD) + + + + + see RIPEMD-128 (https://en.wikipedia.org/wiki/RIPEMD) + + + + + see RIPEMD-128 (https://en.wikipedia.org/wiki/ISO/IEC_10118-3) + + + + + + + A complex type that specifies the encryption used within this element. The saltValue attribute is a base64-encoded binary value that is randomly generated. The number of bytes required to decode the saltValue attribute MUST be equal to the value of the saltSize attribute. + + + + + + + + + + + + + A complex type that specifies data used to verify whether the encrypted data passes an integrity check. It MUST be generated using the method specified in section 2.3.4.14 (http://msdn.microsoft.com/en-us/library/dd924068(v=office.12).aspx). + + + + A base64-encoded value that specifies an encrypted key used in calculating the encryptedHmacValue. + + + + + A base64-encoded value that specifies an HMAC derived from encryptedHmacKey and the encrypted data. + + + + + + modified for POI + A complex type that specifies the parameters used to encrypt an intermediate key, which is used to perform the final encryption of the document. To ensure extensibility, arbitrary elements can be defined to encrypt the intermediate key. The intermediate key MUST be the same for all KeyEncryptor elements. + + + + + + + + modified for POI + + + + + + + + + + + + A sequence of KeyEncryptor elements. Exactly one KeyEncryptors element MUST be present, and the KeyEncryptors element MUST contain at least one KeyEncryptor. + + + + + + + + + + + modified for POI + All ECMA-376 documents [ECMA-376] encrypted by Microsoft Office using agile encryption will have a DataIntegrity element present. The schema allows for a DataIntegrity element to not be present because the encryption schema can be used by applications that do not create ECMA-376 documents [ECMA-376]. + + + + + The KeyEncryptor element, which MUST be used when encrypting password-protected agile encryption documents, is either a PasswordKeyEncryptor or a CertificateKeyEncryptor. Exactly one PasswordKeyEncryptor MUST be present. Zero or more CertificateKeyEncryptor elements are contained within the KeyEncryptors element. + + + + + + diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsdconfig b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsdconfig index c9474a0f3a..cbc24394b8 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsdconfig +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionInfo.xsdconfig @@ -1,25 +1,25 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsd index 79ae888a0e..3ecfc92320 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsd @@ -1,66 +1,66 @@ - - - - - - - - - - - - A SaltSize that specifies the size of the salt for a PasswordKeyEncryptor. - - - A BlockSize that specifies the block size for a PasswordKeyEncryptor. - - - A KeyBits that specifies the number of bits for a PasswordKeyEncryptor. - - - A HashSize that specifies the size of the binary form of the hash for a PasswordKeyEncryptor. - - - A CipherAlgorithm that specifies the cipher algorithm for a PasswordKeyEncryptor. The cipher algorithm specified MUST be the same as the cipher algorithm specified for the Encryption.keyData element. - - - A CipherChaining that specifies the cipher chaining mode for a PasswordKeyEncryptor. - - - A HashAlgorithm that specifies the hashing algorithm for a PasswordKeyEncryptor. The hashing algorithm specified MUST be the same as the hashing algorithm specified for the Encryption.keyData element. - - - A base64-encoded binary byte array that specifies the salt value for a PasswordKeyEncryptor. The number of bytes required by the decoded form of this element MUST be saltSize. - - - A SpinCount that specifies the spin count for a PasswordKeyEncryptor. - - - A base64-encoded value that specifies the encrypted verifier hash input for a PasswordKeyEncryptor used in password verification. - - - A base64-encoded value that specifies the encrypted verifier hash value for a PasswordKeyEncryptor used in password verification. - - - A base64-encoded value that specifies the encrypted form of the intermediate key. - - - - + + + + + + + + + + + + A SaltSize that specifies the size of the salt for a PasswordKeyEncryptor. + + + A BlockSize that specifies the block size for a PasswordKeyEncryptor. + + + A KeyBits that specifies the number of bits for a PasswordKeyEncryptor. + + + A HashSize that specifies the size of the binary form of the hash for a PasswordKeyEncryptor. + + + A CipherAlgorithm that specifies the cipher algorithm for a PasswordKeyEncryptor. The cipher algorithm specified MUST be the same as the cipher algorithm specified for the Encryption.keyData element. + + + A CipherChaining that specifies the cipher chaining mode for a PasswordKeyEncryptor. + + + A HashAlgorithm that specifies the hashing algorithm for a PasswordKeyEncryptor. The hashing algorithm specified MUST be the same as the hashing algorithm specified for the Encryption.keyData element. + + + A base64-encoded binary byte array that specifies the salt value for a PasswordKeyEncryptor. The number of bytes required by the decoded form of this element MUST be saltSize. + + + A SpinCount that specifies the spin count for a PasswordKeyEncryptor. + + + A base64-encoded value that specifies the encrypted verifier hash input for a PasswordKeyEncryptor used in password verification. + + + A base64-encoded value that specifies the encrypted verifier hash value for a PasswordKeyEncryptor used in password verification. + + + A base64-encoded value that specifies the encrypted form of the intermediate key. + + + + diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsdconfig b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsdconfig index 3a2bb2c8e9..5aeabfbc96 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsdconfig +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/encryptionPassword.xsdconfig @@ -1,24 +1,24 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/signatureInfo.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/signatureInfo.xsd index f7019f13f5..e9f55885e0 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/signatureInfo.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/poifs/crypt/signatureInfo.xsd @@ -1,103 +1,103 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdES.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdES.xsd index 4b74692f85..87f7209350 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdES.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdES.xsd @@ -1,466 +1,466 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdESv141.xsd b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdESv141.xsd index 8cb527978b..446d2c1c7e 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdESv141.xsd +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/XAdESv141.xsd @@ -1,15 +1,15 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/ooxmlSchemas.xsdconfig b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/ooxmlSchemas.xsdconfig index a567df5816..a3881c05e6 100644 --- a/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/ooxmlSchemas.xsdconfig +++ b/poi-ooxml-full/src/main/xmlschema/org/apache/poi/schemas/ooxmlSchemas.xsdconfig @@ -1,50 +1,50 @@ - - - - - - com.microsoft.schemas.office.office - - - - com.microsoft.schemas.office.excel - - - - com.microsoft.schemas.office.word - - - - com.microsoft.schemas.office.powerpoint - - - - com.microsoft.schemas.vml - - - - com.microsoft.schemas.compatibility - - - - org.apache.poi.schemas.vmldrawing - - + + + + + + com.microsoft.schemas.office.office + + + + com.microsoft.schemas.office.excel + + + + com.microsoft.schemas.office.word + + + + com.microsoft.schemas.office.powerpoint + + + + com.microsoft.schemas.vml + + + + com.microsoft.schemas.compatibility + + + + org.apache.poi.schemas.vmldrawing + + \ No newline at end of file diff --git a/poi-ooxml-lite-agent/build.gradle b/poi-ooxml-lite-agent/build.gradle index 01df3553c8..547299246b 100644 --- a/poi-ooxml-lite-agent/build.gradle +++ b/poi-ooxml-lite-agent/build.gradle @@ -22,8 +22,8 @@ sourceSets { } dependencies { - api 'net.bytebuddy:byte-buddy:1.18.2' - api 'net.bytebuddy:byte-buddy-agent:1.18.2' + api 'net.bytebuddy:byte-buddy:1.18.5' + api 'net.bytebuddy:byte-buddy-agent:1.18.5' api "org.apache.xmlbeans:xmlbeans:${xmlbeansVersion}" } @@ -56,7 +56,7 @@ tasks.register('compileJava9', JavaCompile) { } jar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes ( diff --git a/poi-ooxml-lite/build.gradle b/poi-ooxml-lite/build.gradle index 71b58b0958..32cd1c0ae3 100644 --- a/poi-ooxml-lite/build.gradle +++ b/poi-ooxml-lite/build.gradle @@ -107,7 +107,7 @@ task compileJava9(type: JavaCompile, dependsOn: 'compileJava') { } jar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") doFirst { File clazzFile = file("${OOXML_LITE_REPORT}.clazz") diff --git a/poi-ooxml/build.gradle b/poi-ooxml/build.gradle index 069691099e..b77d63369f 100644 --- a/poi-ooxml/build.gradle +++ b/poi-ooxml/build.gradle @@ -62,7 +62,7 @@ dependencies { // and uncomment the line below to use a pre-built version of poi-ooxml-full. // Try to use the last release version of poi-ooxml-full. You might be unlucky if // recent unreleased changes in poi-ooxml-full are needed. - // api "org.apache.poi:poi-ooxml-full:5.5.0" + // api "org.apache.poi:poi-ooxml-full:5.5.1" api "org.apache.xmlbeans:xmlbeans:${xmlbeansVersion}" api "org.apache.commons:commons-compress:${commonsCompressVersion}" @@ -187,7 +187,7 @@ tasks.register('compileTest9', JavaCompile) { jar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes('Automatic-Module-Name': MODULE_NAME, 'Multi-Release': 'true') @@ -197,7 +197,7 @@ jar { // Create a separate jar for test-code to depend on it in other projects // See http://stackoverflow.com/questions/5144325/gradle-test-dependency task testJar(type: Jar, dependsOn: testClasses) { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-tests") setArchiveClassifier 'tests' // ignore second module-info.class from main diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java index d34756f245..3c66fc9cfb 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java +++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/HyperlinkRelationship.java @@ -1,44 +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. -==================================================================== */ -package org.apache.poi.ooxml; - -import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; - -import java.net.URI; - -/** - * Represents a hyperlink relationship. - * - * @since 5.3.0 - */ -public class HyperlinkRelationship extends ReferenceRelationship { - /** - * Initializes a new instance of the HyperlinkRelationship. - * - * @param hyperlinkUri The target uri of the hyperlink relationship. - * @param isExternal Is the URI external. - * @param id The relationship ID. - */ - protected HyperlinkRelationship(POIXMLDocumentPart container, URI hyperlinkUri, boolean isExternal, String id) { - super(container, hyperlinkUri, isExternal, PackageRelationshipTypes.HYPERLINK_PART, id); - } - - @Override - public String getRelationshipType() { - return PackageRelationshipTypes.HYPERLINK_PART; - } -} +/* ==================================================================== + 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.poi.ooxml; + +import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; + +import java.net.URI; + +/** + * Represents a hyperlink relationship. + * + * @since 5.3.0 + */ +public class HyperlinkRelationship extends ReferenceRelationship { + /** + * Initializes a new instance of the HyperlinkRelationship. + * + * @param hyperlinkUri The target uri of the hyperlink relationship. + * @param isExternal Is the URI external. + * @param id The relationship ID. + */ + protected HyperlinkRelationship(POIXMLDocumentPart container, URI hyperlinkUri, boolean isExternal, String id) { + super(container, hyperlinkUri, isExternal, PackageRelationshipTypes.HYPERLINK_PART, id); + } + + @Override + public String getRelationshipType() { + return PackageRelationshipTypes.HYPERLINK_PART; + } +} diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java index 64ed50b4bd..56dda3039f 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java +++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/POIXMLDocumentPart.java @@ -45,6 +45,7 @@ import org.apache.poi.xssf.usermodel.XSSFRelation; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFRelation; +import org.apache.xmlbeans.XmlException; /** * Represents an entry of a OOXML package. @@ -258,7 +259,7 @@ public class POIXMLDocumentPart { */ public final POIXMLDocumentPart getRelationById(String id) { RelationPart rp = getRelationPartById(id); - return (rp == null) ? null : rp.getDocumentPart(); + return rp == null ? null : rp.getDocumentPart(); } /** @@ -668,8 +669,22 @@ public class POIXMLDocumentPart { POIXMLDocumentPart childPart = context.get(p); if (childPart == null) { - childPart = factory.createDocumentPart(this, p); - //here we are checking if part if embedded and excel then set it to chart class + try { + childPart = factory.createDocumentPart(this, p); + } catch (POIXMLException e) { + if (e.getCause() instanceof XmlException + && XSSFRelation.CHART.getRelation().equals(rel.getRelationshipType())) { + // https://github.com/apache/poi/pull/982 + // only allow this skipping event for charts + // we need to be careful about not catching every exception here because + // issues like zip bomb exceptions need to thrown and not ignored + LOG.atWarn().log("Skipped unsupported part: {}", e.getMessage()); + continue; + } else { + throw e; + } + } + //here we are checking if part is embedded and excel then set it to chart class //so that at the time to writing we can also write updated embedded part if (this instanceof XDDFChart && childPart instanceof XSSFWorkbook) { ((XDDFChart) this).setWorkbook((XSSFWorkbook) childPart); @@ -782,7 +797,7 @@ public class POIXMLDocumentPart { * @since 5.3.0 */ public final HyperlinkRelationship createHyperlink(URI uri, boolean isExternal, String relId) { - PackageRelationship pr = packagePart.addRelationship(uri, isExternal ? TargetMode.EXTERNAL : TargetMode.INTERNAL, + packagePart.addRelationship(uri, isExternal ? TargetMode.EXTERNAL : TargetMode.INTERNAL, PackageRelationshipTypes.HYPERLINK_PART, relId); HyperlinkRelationship hyperlink = new HyperlinkRelationship(this, uri, isExternal, relId); referenceRelationships.put(relId, hyperlink); diff --git a/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java b/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java index c802157f44..336a6e7346 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java +++ b/poi-ooxml/src/main/java/org/apache/poi/ooxml/ReferenceRelationship.java @@ -1,79 +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. -==================================================================== */ -package org.apache.poi.ooxml; - -import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.openxml4j.opc.TargetMode; - -import java.net.URI; - -/** - * Defines a reference relationship. A reference relationship can be internal or external. - * - * @since 5.3.0 - */ -public abstract class ReferenceRelationship { - private POIXMLDocumentPart container; - private final String relationshipType; - private final boolean external; - private final String id; - private final URI uri; - - protected ReferenceRelationship(POIXMLDocumentPart container, PackageRelationship packageRelationship) { - if (packageRelationship == null) { - throw new IllegalArgumentException("packageRelationship"); - } - - this.container = container; - this.relationshipType = packageRelationship.getRelationshipType(); - this.uri = packageRelationship.getTargetURI(); - this.external = packageRelationship.getTargetMode() == TargetMode.EXTERNAL; - this.id = packageRelationship.getId(); - } - - protected ReferenceRelationship(POIXMLDocumentPart container, URI targetUri, boolean isExternal, String relationshipType, String id) { - if (targetUri == null) { - throw new NullPointerException("targetUri cannot be null"); - } - - this.container = container; - this.relationshipType = relationshipType; - this.uri = targetUri; - this.id = id; - this.external = isExternal; - } - - public POIXMLDocumentPart getContainer() { - return container; - } - - public String getRelationshipType() { - return relationshipType; - } - - public boolean isExternal() { - return external; - } - - public String getId() { - return id; - } - - public URI getUri() { - return uri; - } -} +/* ==================================================================== + 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.poi.ooxml; + +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.TargetMode; + +import java.net.URI; + +/** + * Defines a reference relationship. A reference relationship can be internal or external. + * + * @since 5.3.0 + */ +public abstract class ReferenceRelationship { + private POIXMLDocumentPart container; + private final String relationshipType; + private final boolean external; + private final String id; + private final URI uri; + + protected ReferenceRelationship(POIXMLDocumentPart container, PackageRelationship packageRelationship) { + if (packageRelationship == null) { + throw new IllegalArgumentException("packageRelationship"); + } + + this.container = container; + this.relationshipType = packageRelationship.getRelationshipType(); + this.uri = packageRelationship.getTargetURI(); + this.external = packageRelationship.getTargetMode() == TargetMode.EXTERNAL; + this.id = packageRelationship.getId(); + } + + protected ReferenceRelationship(POIXMLDocumentPart container, URI targetUri, boolean isExternal, String relationshipType, String id) { + if (targetUri == null) { + throw new NullPointerException("targetUri cannot be null"); + } + + this.container = container; + this.relationshipType = relationshipType; + this.uri = targetUri; + this.id = id; + this.external = isExternal; + } + + public POIXMLDocumentPart getContainer() { + return container; + } + + public String getRelationshipType() { + return relationshipType; + } + + public boolean isExternal() { + return external; + } + + public String getId() { + return id; + } + + public URI getUri() { + return uri; + } +} diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java index afdb5a5800..fcb774d4f7 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java +++ b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java @@ -1875,7 +1875,10 @@ public abstract class OPCPackage implements RelationshipSource, Closeable { public abstract boolean isClosed(); protected void closeParts() { - partList.closeParts(); + // might not be initialized fully yet + if (partList != null) { + partList.closeParts(); + } } @Override diff --git a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java index e6a9731b66..2ec8d394a9 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java @@ -47,6 +47,10 @@ public class RelLineTo implements GeometryRow { for (CellType cell : row.getCellArray()) { String cellName = cell.getN(); + if (cellName == null) { + throw new IllegalStateException("Cannot create a ReLineTo object without a cell-name indicating 'X' or 'Y' direction"); + } + if (cellName.equals("X")) { x = XDGFCell.parseDoubleValue(cell); } else if (cellName.equals("Y")) { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 63cea1a76c..44585f8b77 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -748,4 +748,51 @@ public class XMLSlideShow extends POIXMLDocument RelationPart rp = target.addRelation(null, XSLFRelation.IMAGES, pictureData); return rp.getRelationship().getId(); } + + /** + * Returns the number that shall be displayed on the first slide of the presentation. + * Subsequent slides will be numbered sequentially from this number. + * The default value is 1 if the property is not set in the presentation file. + * + * @return The starting number for the first slide (default is 1). + * @since 6.0.0 + */ + public int getFirstSlideNumber() { + // CTPresentation.getFirstSlideNum() returns the default value of 1 + // if the 'firstSlideNum' attribute is not set, as per OOXML standard. + return getCTPresentation().getFirstSlideNum(); + } + + /** + * Sets the custom number that shall be displayed on the first slide of the presentation. + * Subsequent slides will be numbered sequentially from this number. + * + * The value is restricted to the range [0, 9999] as defined in the Microsoft Office + * Implementation Information for ISO/IEC 29500 (MS-OI29500), specifically in + * Part 1, Section 19.2.1.26, presentation (Presentation), which restricts the + * XML Schema int datatype. + * + * @param num The starting number for the first slide (must be between 0 and 9999). + * @throws IllegalArgumentException if the provided number is outside the allowed range [0, 9999]. + * @since 6.0.0 + */ + public void setFirstSlideNumber(int num) { + // We enforce the PowerPoint application constraint (0 <= num <= 9999) + // to ensure that a valid file, as rendered by PowerPoint, is created. + if (num < 0 || num > 9999) { + throw new IllegalArgumentException( + "First slide number must be between 0 and 9999 (inclusive), as required by MS-OI29500 (Part 1, Section 19.2.1.26)."); + } + // Sets the attribute on the underlying CTPresentation object. + getCTPresentation().setFirstSlideNum(num); + } + + /** + * Unsets the custom first slide number, reverting the numbering to the default (1). + * This removes the 'firstSlideNum' attribute from presentation.xml. + * @since 6.0.0 + */ + public void unsetFirstSlideNumber() { + getCTPresentation().unsetFirstSlideNum(); + } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java index 3bba3c5032..4284d7e815 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java @@ -44,7 +44,7 @@ public class XSLFAutoShape extends XSLFTextShape implements AutoShape 0 ? POIXMLUnits.parsePercent(blip.getAlphaModFixArray(0).xgetAmt()) : FULLY_OPAQUE_ALPHA_VALUE; + } + /** * Add a SVG image reference * @param svgPic a previously imported svg image diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 4d3c78b933..6a1c6bb4ce 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -535,7 +535,7 @@ public abstract class XSLFSimpleShape extends XSLFShape @Override public boolean fetch(XSLFShape shape) { CTLineProperties ln = getLn(shape, false); - if (ln == null || !ln.isSetPrstDash()) { + if (ln == null || !ln.isSetPrstDash() || ln.getPrstDash().getVal() == null) { return false; } @@ -548,7 +548,7 @@ public abstract class XSLFSimpleShape extends XSLFShape LineDash dash = fetcher.getValue(); if (dash == null) { CTLineProperties defaultLn = getDefaultLineProperties(); - if (defaultLn != null && defaultLn.isSetPrstDash()) { + if (defaultLn != null && defaultLn.isSetPrstDash() && defaultLn.getPrstDash().getVal() != null) { dash = LineDash.fromOoxmlId(defaultLn.getPrstDash().getVal().intValue()); } } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTable.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTable.java index eb02744437..eeb48af8a7 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTable.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFTable.java @@ -340,6 +340,9 @@ public class XSLFTable extends XSLFGraphicFrame implements IterableSelect this overload when the consumer expects formatted string values rather than raw * cell representations. * @@ -224,6 +224,10 @@ public class XSSFBSheetHandler extends XSSFBParser { } private ExcelNumberFormat getExcelNumberFormat() { + if (styles == null) { + throw new IllegalStateException("Cannot read information because styles-table is not available"); + } + int styleIdx = cellBuffer.getStyleIdx(); String formatString = styles.getNumberFormatString(styleIdx); short styleIndex = styles.getNumberFormatIndex(styleIdx); @@ -399,7 +403,7 @@ public class XSSFBSheetHandler extends XSSFBParser { rkBuffer[4] = b0; System.arraycopy(data, offset + 1, rkBuffer, 5, 3); - double d = 0.0; + double d; if (floatingPoint) { d = LittleEndian.getDouble(rkBuffer); } else { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java index de4dbb207c..f945b3457f 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java @@ -436,8 +436,16 @@ public class XSSFReader { sheetPkg.getRelationshipsByType(XSSFRelation.SHEET_COMMENTS.getRelation()); if (!commentsList.isEmpty()) { PackageRelationship comments = commentsList.getRelationship(0); + if (comments == null) { + LOGGER.warn("Failed to find sheet comments packageRelationship"); + return null; + } PackagePartName commentsName = PackagingURIHelper.createPartName(comments.getTargetURI()); PackagePart commentsPart = sheetPkg.getPackage().getPart(commentsName); + if (commentsPart == null) { + LOGGER.warn("Failed to find sheet comments: {}", commentsName); + return null; + } return parseComments(commentsPart); } } catch (InvalidFormatException|IOException e) { diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java index 65d0f6c3d5..4ebdf83fac 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java @@ -156,7 +156,7 @@ public class XSSFExportToXml implements Comparator{ indexMap.clear(); xpaths.sort(this); indexMap.clear(); - + for(String xpath : xpaths) { XSSFSingleXmlCell simpleXmlCell = singleXmlCellsMappings.get(xpath); @@ -170,7 +170,7 @@ public class XSSFExportToXml implements Comparator{ if (cell!=null) { Node currentNode = getNodeByXPath(xpath,doc.getFirstChild(),doc,false); mapCellOnNode(cell,currentNode); - + //remove nodes which are empty in order to keep the output xml valid // FIXME: what should be done if currentNode.getTextContent() is null? if ("".equals(currentNode.getTextContent()) && currentNode.getParentNode() != null) { @@ -192,6 +192,11 @@ public class XSSFExportToXml implements Comparator{ for(int i = startRow; i<= endRow; i++) { XSSFRow row = sheet.getRow(i); + // some external created files can miss rows + if (row == null) { + continue; + } + Node tableRootNode = getNodeByXPath(table.getCommonXpath(), doc.getFirstChild(), doc, true); short startColumnIndex = table.getStartCellReference().getCol(); @@ -255,7 +260,7 @@ public class XSSFExportToXml implements Comparator{ Schema schema = factory.newSchema(source); Validator validator = schema.newValidator(); validator.validate(new DOMSource(xml)); - + //if no exceptions where raised, the document is valid return true; } catch(IOException e) { @@ -289,8 +294,8 @@ public class XSSFExportToXml implements Comparator{ } } break; - - case NUMERIC: + + case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { value = getFormattedDate(cell); } else { @@ -459,7 +464,7 @@ public class XSSFExportToXml implements Comparator{ } return Integer.compare(leftIndexOf, rightIndexOf); } - + private int getAndStoreIndex(String samePath,String withoutNamespace) { String withPath = samePath+"/"+withoutNamespace; return indexMap.getOrDefault(withPath, -1); @@ -470,7 +475,7 @@ public class XSSFExportToXml implements Comparator{ if(returnNode != null) { return returnNode; } - + return node.getAttributes().getNamedItem("name"); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java index d23375fda5..d9da22cefc 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -219,6 +219,7 @@ public final class XSSFCell extends CellBase { */ @Override public void setCellValue(boolean value) { + removeInlineString(); _cell.setT(STCellType.B); _cell.setV(value ? TRUE_AS_STRING : FALSE_AS_STRING); } @@ -263,6 +264,7 @@ public final class XSSFCell extends CellBase { @Override public void setCellValueImpl(double value) { + removeInlineString(); _cell.setT(STCellType.N); _cell.setV(String.valueOf(value)); } @@ -356,27 +358,23 @@ public final class XSSFCell extends CellBase { @Override protected void setCellValueImpl(RichTextString str) { + removeInlineString(); CellType cellType = getCellType(); if (cellType == CellType.FORMULA) { _cell.setV(str.getString()); _cell.setT(STCellType.STR); + } else if (str instanceof XSSFRichTextString) { + _cell.setT(STCellType.S); + XSSFRichTextString rt = (XSSFRichTextString)str; + rt.setStylesTableReference(_stylesSource); + int sRef = _sharedStringSource.addSharedStringItem(rt); + _cell.setV(Integer.toString(sRef)); } else { - if(_cell.getT() == STCellType.INLINE_STR) { - //set the 'pre-evaluated result - _cell.setV(str.getString()); - } else if (str instanceof XSSFRichTextString) { - _cell.setT(STCellType.S); - XSSFRichTextString rt = (XSSFRichTextString)str; - rt.setStylesTableReference(_stylesSource); - int sRef = _sharedStringSource.addSharedStringItem(rt); - _cell.setV(Integer.toString(sRef)); - } else { - _cell.setT(STCellType.S); - XSSFRichTextString rt = new XSSFRichTextString(str.getString()); - rt.setStylesTableReference(_stylesSource); - int sRef = _sharedStringSource.addSharedStringItem(rt); - _cell.setV(Integer.toString(sRef)); - } + _cell.setT(STCellType.S); + XSSFRichTextString rt = new XSSFRichTextString(str.getString()); + rt.setStylesTableReference(_stylesSource); + int sRef = _sharedStringSource.addSharedStringItem(rt); + _cell.setV(Integer.toString(sRef)); } } @@ -410,7 +408,9 @@ public final class XSSFCell extends CellBase { /* In an excel generated array formula, the formula property might be set, but the string is empty in related cells */ if (f == null || f.getStringValue().isEmpty()) { XSSFCell cell = getSheet().getFirstCellInArrayFormula(this); - return cell.getCellFormula(fpb); + if (!equals(cell)) { + return cell.getCellFormula(fpb); + } } } if (f == null) { @@ -519,6 +519,12 @@ public final class XSSFCell extends CellBase { } } + private void removeInlineString() { + if (_cell.isSetIs()) { + _cell.unsetIs(); + } + } + /** * Returns column index of this cell * diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFPicture.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFPicture.java index fe59d4c6b6..da377e7ab9 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFPicture.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFPicture.java @@ -263,10 +263,15 @@ public final class XSSFPicture extends XSSFShape implements Picture { /** * Return picture data for this shape * - * @return picture data for this shape + * @return picture data for this shape or null if + * the data cannot be retrieved */ @Override public XSSFPictureData getPictureData() { + if (ctPicture.getBlipFill().getBlip() == null) { + return null; + } + String blipId = ctPicture.getBlipFill().getBlip().getEmbed(); return (XSSFPictureData)getDrawing().getRelationById(blipId); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTable.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTable.java index 15cd3bbaf8..8b279fc720 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTable.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFTable.java @@ -211,7 +211,9 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { List columns = new ArrayList<>(); CTTableColumns ctTableColumns = ctTable.getTableColumns(); if (ctTableColumns != null) { - for (CTTableColumn column : ctTableColumns.getTableColumnList()) { + // Use Array and not List-based access, as list-iteration is + // very slow for large tables + for (CTTableColumn column : ctTableColumns.getTableColumnArray()) { XSSFTableColumn tableColumn = new XSSFTableColumn(this, column); columns.add(tableColumn); } @@ -308,11 +310,12 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { tableStart.getCol() + newColumnCount - 1); AreaReference newTableArea = new AreaReference(tableStart, newTableEnd, version); + // setCellRef also calls updateHeaders() setCellRef(newTableArea); + } else { + updateHeaders(); } - updateHeaders(); - return getColumns().get(columnIndex); } @@ -353,7 +356,9 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { CTTableColumns tableColumns = ctTable.getTableColumns(); tableColumns.removeTableColumn(columnIndex); - tableColumns.setCount(tableColumns.getTableColumnList().size()); + // Use Array and not List-based access, as list-iteration is + // very slow for large tables + tableColumns.setCount(tableColumns.getTableColumnArray().length); updateReferences(); updateHeaders(); } @@ -406,7 +411,7 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { try { ctTable.getTableStyleInfo().unsetName(); } catch (Exception e) { - LOG.atDebug().log("Failed to unset style name", e); + LOG.atDebug().log("Failed to unset style name: {}", e); } } styleName = null; @@ -821,13 +826,19 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { int cellnum = firstHeaderColumn; CTTableColumns ctTableColumns = getCTTable().getTableColumns(); if(ctTableColumns != null) { - for (CTTableColumn col : ctTableColumns.getTableColumnList()) { + // Use Array and not List-based access, as list-iteration is + // very slow for large tables + for (CTTableColumn col : ctTableColumns.getTableColumnArray()) { XSSFCell cell = row.getCell(cellnum); if (cell != null) { String colName = formatter.formatCellValue(cell); colName = colName.replace("\n", "_x000a_"); colName = colName.replace("\r", "_x000d_"); - col.setName(colName); + + // setName() is costly, let's only run it when necessary + if (!colName.equals(col.getName())) { + col.setName(colName); + } } cellnum++; } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index a62bbfb739..1edd31d614 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -256,15 +256,24 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su */ public XSSFWorkbook(OPCPackage pkg) throws IOException { super(pkg); - this.xssfFactory = XSSFFactory.getInstance(); - beforeDocumentRead(); + try { + this.xssfFactory = XSSFFactory.getInstance(); - // Build a tree of POIXMLDocumentParts, this workbook being the root - load(this.xssfFactory); + beforeDocumentRead(); - // some broken Workbooks miss this... - setBookViewsIfMissing(); + // Build a tree of POIXMLDocumentParts, this workbook being the root + load(this.xssfFactory); + + // some broken Workbooks miss this... + setBookViewsIfMissing(); + } catch (IOException | RuntimeException e) { + // close the package on exception to avoid file handle leaks + pkg.revert(); + + // rethrow exception + throw e; + } } /** @@ -1218,6 +1227,10 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su */ @Override public XSSFSheet getSheet(String name) { + if (name == null) { + throw new IllegalArgumentException("Encountered an empty name when looking up sheets by name"); + } + for (XSSFSheet sheet : sheets) { if (name.equalsIgnoreCase(sheet.getSheetName())) { return sheet; diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java index 11b95d2ee7..6dcee13193 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java @@ -38,7 +38,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; */ public class ColumnHelper { - private CTWorksheet worksheet; + private final CTWorksheet worksheet; public ColumnHelper(CTWorksheet worksheet) { super(); @@ -264,9 +264,10 @@ public class ColumnHelper { CTCol col = getOrCreateColumn1Based(index+1, false); col.setBestFit(bestFit); } - public void setCustomWidth(long index, boolean bestFit) { + + public void setCustomWidth(long index, boolean useCustomWidth) { CTCol col = getOrCreateColumn1Based(index+1, true); - col.setCustomWidth(bestFit); + col.setCustomWidth(useCustomWidth); } public void setColWidth(long index, double width) { @@ -302,10 +303,15 @@ public class ColumnHelper { col.setStyle(styleId); } - // Returns -1 if no column is found for the given index + /** + * Get the default style index for the given column index + * + * @return The style-index or -1 if no column is found for the given index + */ public int getColDefaultStyle(long index) { - if (getColumn(index, false) != null) { - return (int) getColumn(index, false).getStyle(); + final CTCol column = getColumn(index, false); + if (column != null) { + return (int) column.getStyle(); } return -1; } @@ -330,4 +336,4 @@ public class ColumnHelper { } return -1; } -} \ No newline at end of file +} diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java index 549a746fc8..2c3d6d7626 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java @@ -123,6 +123,11 @@ public class XWPFEndnotes extends XWPFAbstractFootnotesEndnotes { @Override protected void commit() throws IOException { + // cannot save anything if class is not initialized fully + if (ctEndnotes == null) { + return; + } + XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); xmlOptions.setSaveSyntheticDocumentElement(new QName(CTEndnotes.type.getName().getNamespaceURI(), "endnotes")); PackagePart part = getPackagePart(); diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java index fc0bd3981f..ace3e0271e 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java @@ -17,6 +17,7 @@ package org.apache.poi.xwpf.usermodel; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.poi.util.Beta; @@ -107,6 +108,15 @@ public class XWPFSDTContent implements ISDTContent { } } + /** + * Get the body elements inside this SDT content element. + * @return Unmodifiable list containing body elements. + * @since 6.0.0 + */ + public List getBodyElements() { + return Collections.unmodifiableList(bodyElements); + } + @Override public String getText() { StringBuilder text = new StringBuilder(); diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java index 735b930c88..a01904e835 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTheme.java @@ -27,7 +27,9 @@ import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTBaseStyles; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme; +import org.openxmlformats.schemas.drawingml.x2006.main.CTFontCollection; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSupplementalFont; import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; @@ -67,11 +69,19 @@ public class XWPFTheme extends POIXMLDocumentPart { _theme = theme.getXmlObject(); } + /** + * @return the underlying CTOfficeStyleSheet instance. + * @since 6.0.0 + */ + public CTOfficeStyleSheet getCTOfficeStyleSheet() { + return _theme; + } + /** * * @return name of this theme, e.g. "Office Theme" */ - public String getName(){ + public String getName() { return _theme.getName(); } @@ -80,7 +90,7 @@ public class XWPFTheme extends POIXMLDocumentPart { * * @param name name of this theme */ - public void setName(String name){ + public void setName(String name) { _theme.setName(name); } @@ -132,23 +142,79 @@ public class XWPFTheme extends POIXMLDocumentPart { } /** - * @return typeface of the major font to use in a document. + * @return typeface of the major font to use in a document (possibly null). * Typically, the major font is used for heading areas of a document. * */ @SuppressWarnings("WeakerAccess") - public String getMajorFont(){ - return _theme.getThemeElements().getFontScheme().getMajorFont().getLatin().getTypeface(); + public String getMajorFont() { + CTFontCollection majorFonts = getMajorFonts(); + return majorFonts == null || majorFonts.getLatin() == null ? + null : majorFonts.getLatin().getTypeface(); } /** - * @return typeface of the minor font to use in a document. + * @return typeface of the minor font to use in a document (possibly null). * Typically, the minor font is used for normal text or paragraph areas. * */ @SuppressWarnings("WeakerAccess") - public String getMinorFont(){ - return _theme.getThemeElements().getFontScheme().getMinorFont().getLatin().getTypeface(); + public String getMinorFont() { + CTFontCollection minorFonts = getMinorFonts(); + return minorFonts == null || minorFonts.getLatin() == null ? + null : minorFonts.getLatin().getTypeface(); + } + + /** + * @param script a 4-letter script code, e.g. "Latn", "Jpan" + * @return typeface of the major font for the given script (possibly null) + * @since 6.0.0 + */ + public String getMajorFontForScript(String script) { + CTFontCollection majorFonts = getMajorFonts(); + return majorFonts == null ? null : getFontTypeface(majorFonts, script); + } + + /** + * @param script a 4-letter script code, e.g. "Latn", "Jpan" + * @return typeface of the minor font for the given script (possibly null) + * @since 6.0.0 + */ + public String getMinorFontForScript(String script) { + CTFontCollection minorFonts = getMinorFonts(); + return minorFonts == null ? null : getFontTypeface(minorFonts, script); + } + + private CTFontCollection getMajorFonts() { + if (_theme == null + || _theme.getThemeElements() == null + || _theme.getThemeElements().getFontScheme() == null + || _theme.getThemeElements().getFontScheme().getMajorFont() == null) { + return null; + } + return _theme.getThemeElements().getFontScheme().getMajorFont(); + } + + private CTFontCollection getMinorFonts() { + if (_theme == null + || _theme.getThemeElements() == null + || _theme.getThemeElements().getFontScheme() == null + || _theme.getThemeElements().getFontScheme().getMinorFont() == null) { + return null; + } + return _theme.getThemeElements().getFontScheme().getMinorFont(); + } + + private static String getFontTypeface(CTFontCollection fontCollection, String script) { + CTSupplementalFont[] fonts = fontCollection.getFontArray(); + if (fonts != null) { + for (CTSupplementalFont font : fonts) { + if (font.getScript() != null && font.getScript().equals(script)) { + return font.getTypeface(); + } + } + } + return null; } /** diff --git a/poi-ooxml/src/main/resources/org/apache/poi/xslf/usermodel/notesMaster.xml b/poi-ooxml/src/main/resources/org/apache/poi/xslf/usermodel/notesMaster.xml index 0a8db65bd8..7f56d49c6b 100644 --- a/poi-ooxml/src/main/resources/org/apache/poi/xslf/usermodel/notesMaster.xml +++ b/poi-ooxml/src/main/resources/org/apache/poi/xslf/usermodel/notesMaster.xml @@ -1,2 +1,2 @@ - + 1.7.2013Click to edit Master text stylesSecond levelThird levelFourth levelFifth level‹#› \ No newline at end of file diff --git a/poi-ooxml/src/main/resources/org/apache/poi/xssf/usermodel/presetTableStyles.xml b/poi-ooxml/src/main/resources/org/apache/poi/xssf/usermodel/presetTableStyles.xml index f83f2e33c1..7f796bac7e 100644 --- a/poi-ooxml/src/main/resources/org/apache/poi/xssf/usermodel/presetTableStyles.xml +++ b/poi-ooxml/src/main/resources/org/apache/poi/xssf/usermodel/presetTableStyles.xml @@ -1,4 +1,4 @@ - + diff --git a/poi-ooxml/src/test/java/org/apache/poi/ooxml/TestDetectAsOOXML.java b/poi-ooxml/src/test/java/org/apache/poi/ooxml/TestDetectAsOOXML.java index 1255f1da3f..bc83fb7386 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/ooxml/TestDetectAsOOXML.java +++ b/poi-ooxml/src/test/java/org/apache/poi/ooxml/TestDetectAsOOXML.java @@ -19,7 +19,7 @@ package org.apache.poi.ooxml; import static org.apache.poi.hssf.HSSFTestDataSamples.openSampleFileStream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.ByteArrayInputStream; @@ -28,7 +28,6 @@ import java.io.InputStream; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; import org.apache.poi.poifs.filesystem.FileMagic; import org.apache.poi.util.IOUtils; import org.junit.jupiter.api.Test; @@ -66,7 +65,7 @@ class TestDetectAsOOXML { InputStream is = FileMagic.prepareToCheckMagic(testInput); // detect header - assertFalse(FileMagic.valueOf(is) == FileMagic.OOXML); + assertNotEquals(FileMagic.OOXML, FileMagic.valueOf(is)); // check if InputStream is still intact byte[] act = IOUtils.toByteArray(is); diff --git a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/TestOPCPackage.java b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/TestOPCPackage.java index 3a2d9bdcd3..30d2a2bf15 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/TestOPCPackage.java +++ b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/TestOPCPackage.java @@ -19,12 +19,21 @@ package org.apache.poi.openxml4j; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.ooxml.TrackingInputStream; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartCollection; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.ZipPackage; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.io.OutputStream; + class TestOPCPackage { @Test void testPackageCloseClosesInputStream() throws Exception { @@ -48,4 +57,10 @@ class TestOPCPackage { } } + @Test + void testCloseWithEmptyList() throws IOException { + OPCPackage pkg = new ZipPackage(); + + pkg.close(); + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/sl/tests/draw/TestDrawPictureShape.java b/poi-ooxml/src/test/java/org/apache/poi/sl/tests/draw/TestDrawPictureShape.java index 9f5568c4c4..0bd9414d8b 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/sl/tests/draw/TestDrawPictureShape.java +++ b/poi-ooxml/src/test/java/org/apache/poi/sl/tests/draw/TestDrawPictureShape.java @@ -22,8 +22,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeFalse; -import java.awt.Dimension; +import java.awt.*; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -117,4 +118,33 @@ class TestDrawPictureShape { return val; } } + + @Test + void testAlphaXSLFPictureShape() throws IOException { + SlideShow ss = openSampleDocument("picture-transparency.pptx"); + + // First slide contains a fully opaque bitmap + verifySlideFirstPixelColor(ss.getSlides().get(0), new Color(0, 0, 0, 255)); + // Second slide contains a 20% transparency bitmap (255*0.8=204) + verifySlideFirstPixelColor(ss.getSlides().get(1), new Color(0, 0, 0, 204)); + // Third slide contains a 60% transparency bitmap (255*0.4=102) + verifySlideFirstPixelColor(ss.getSlides().get(2), new Color(0, 0, 0, 102)); + // Fourth slide contains a fully transparent bitmap + verifySlideFirstPixelColor(ss.getSlides().get(3), new Color(0, 0, 0, 0)); + } + + private void verifySlideFirstPixelColor(Slide slide, Color color) { + PictureShape picShape = null; + for (Shape shape : slide.getShapes()) { + if (shape instanceof PictureShape) { + picShape = (PictureShape)shape; + break; + } + } + assertNotNull(picShape); + BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + new DrawPictureShape(picShape).draw(img.createGraphics()); + assertEquals(Transparency.TRANSLUCENT, img.getTransparency()); + assertEquals(color, new Color(img.getRGB(0, 0), true)); + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/draw/geom/TestXSLFArcTo.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/draw/geom/TestXSLFArcTo.java new file mode 100644 index 0000000000..44fe8f0b3b --- /dev/null +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/draw/geom/TestXSLFArcTo.java @@ -0,0 +1,62 @@ +/* ==================================================================== + 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.poi.xslf.draw.geom; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.awt.geom.Path2D; + +import org.apache.poi.sl.draw.geom.ArcToCommand; +import org.apache.poi.sl.draw.geom.Context; +import org.apache.poi.sl.draw.geom.CustomGeometry; +import org.junit.jupiter.api.Test; + +class TestXSLFArcTo { + @Test + void test() { + ArcToCommand arc = new ArcToCommand(); + CustomGeometry geom = new CustomGeometry(); + Context ctx = new Context(geom, null, null) { + + @Override + public double getValue(String key) { + return 1.0; + } + }; + + Path2D.Double path = new Path2D.Double(); + path.moveTo(1.0, 1.0); + path.lineTo(2.0, 2.0); + arc.execute(path, ctx); + } + + @Test + void testPointFails() { + ArcToCommand arc = new ArcToCommand(); + CustomGeometry geom = new CustomGeometry(); + Context ctx = new Context(geom, null, null) { + + @Override + public double getValue(String key) { + return 1.0; + } + }; + + assertThrows(IllegalStateException.class, + () -> arc.execute(new Path2D.Double(), ctx)); + } +} diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java index 45e2aa31c2..312c804a3f 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java @@ -16,10 +16,6 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; - import java.awt.Dimension; import java.awt.Rectangle; import java.io.ByteArrayInputStream; @@ -34,6 +30,12 @@ import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.xslf.XSLFTestDataSamples; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + class TestXSLFSlideShow { @Test void testCreateSlide() throws IOException { @@ -327,4 +329,96 @@ class TestXSLFSlideShow { } } } + + /** + * Test getting the first slide number, which defaults to 1. + * The first slide number is stored in the presentation.xml part. + */ + @Test + void testGetFirstSlideNumberDefault() throws IOException { + try (XMLSlideShow ppt = new XMLSlideShow()) { + // Default value when attribute is not present should be 1 (as per OOXML standard and CTPresentation logic) + assertEquals(1, ppt.getFirstSlideNumber()); + } + } + + /** + * Test setting, getting, and persisting the custom first slide number. + */ + @Test + void testSetAndPersistFirstSlideNumber() throws IOException { + final int customStartNum = 5; + final int zeroStartNum = 0; + final int maxStartNum = 9999; + + try (XMLSlideShow ppt = new XMLSlideShow()) { + // 1. Set to a custom positive number (e.g., 5) + ppt.setFirstSlideNumber(customStartNum); + assertEquals(customStartNum, ppt.getFirstSlideNumber()); + + // Check persistence by writing out and reading back + try (XMLSlideShow ppt2 = XSLFTestDataSamples.writeOutAndReadBack(ppt)) { + assertEquals(customStartNum, ppt2.getFirstSlideNumber(), + "The custom first slide number should persist after save/load."); + } + + // 2. Set to minimum valid number (0) + ppt.setFirstSlideNumber(zeroStartNum); + assertEquals(zeroStartNum, ppt.getFirstSlideNumber()); + + // 3. Set to maximum valid number (9999) + ppt.setFirstSlideNumber(maxStartNum); + assertEquals(maxStartNum, ppt.getFirstSlideNumber()); + } + } + + /** + * Test unsetting the first slide number, which should revert it to the default (1). + */ + @Test + void testUnsetFirstSlideNumber() throws IOException { + try (XMLSlideShow ppt = new XMLSlideShow()) { + // 1. Set a custom number + ppt.setFirstSlideNumber(50); + assertEquals(50, ppt.getFirstSlideNumber()); + + // 2. Unset it + ppt.unsetFirstSlideNumber(); + + // It should return the default value (1) after unsetting + assertEquals(1, ppt.getFirstSlideNumber(), + "Unsetting the first slide number should revert it to the default of 1."); + + // Check persistence after unsetting + try (XMLSlideShow ppt2 = XSLFTestDataSamples.writeOutAndReadBack(ppt)) { + assertEquals(1, ppt2.getFirstSlideNumber(), + "The first slide number should remain the default (1) after unset and save/load."); + // Ensure the attribute is actually removed from the CTPresentation object + // Note: We access the internal object to confirm removal, which is acceptable in test code. + assertFalse(ppt2.getCTPresentation().isSetFirstSlideNum(), + "The 'firstSlideNum' attribute should be unset (removed) in the XML."); + } + } + } + + /** + * Test that setting an invalid first slide number (outside [0, 9999]) throws an exception. + */ + @Test + void testSetFirstSlideNumberValidation() throws IOException { + try (XMLSlideShow ppt = new XMLSlideShow()) { + // Test case: Negative number + assertThrows(IllegalArgumentException.class, () -> + ppt.setFirstSlideNumber(-1), + "Negative number should throw IllegalArgumentException."); + + // Test case: Number greater than 9999 + assertThrows(IllegalArgumentException.class, () -> + ppt.setFirstSlideNumber(10000), + "Number greater than 9999 should throw IllegalArgumentException."); + + // Ensure the value hasn't changed from the default after failed attempts + assertEquals(1, ppt.getFirstSlideNumber()); + } + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTable.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTable.java index 88627ad6a1..9529309a2a 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTable.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTable.java @@ -19,6 +19,7 @@ package org.apache.poi.xslf.usermodel; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -36,12 +37,8 @@ import java.io.IOException; import java.util.List; import org.apache.poi.sl.draw.DrawTableShape; -import org.apache.poi.sl.usermodel.ShapeType; -import org.apache.poi.sl.usermodel.Slide; -import org.apache.poi.sl.usermodel.StrokeStyle; +import org.apache.poi.sl.usermodel.*; import org.apache.poi.sl.usermodel.TableCell.BorderEdge; -import org.apache.poi.sl.usermodel.TextParagraph; -import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.util.RandomSingleton; import org.apache.poi.util.TempFile; import org.apache.poi.xslf.XSLFTestDataSamples; @@ -84,6 +81,7 @@ class TestXSLFTable { tab.removeColumn(0); tab.removeColumn(tab.getNumberOfColumns() - 1); assertEquals(data[0].length, tab.getNumberOfColumns()); + assertNull(tab.getTableStyle()); int startRow = rowIdx-1; @@ -167,7 +165,7 @@ class TestXSLFTable { XSLFSlide slide = ppt.getSlides().get(3); List shapes = slide.getShapes(); assertEquals(1, shapes.size()); - assertTrue(shapes.get(0) instanceof XSLFTable); + assertInstanceOf(XSLFTable.class, shapes.get(0)); XSLFTable tbl = (XSLFTable)shapes.get(0); assertEquals(3, tbl.getNumberOfColumns()); assertEquals(6, tbl.getNumberOfRows()); @@ -179,6 +177,7 @@ class TestXSLFTable { assertEquals(90.0, tbl.getColumnWidth(0), 0); assertEquals(240.0, tbl.getColumnWidth(1), 0); assertEquals(150.0, tbl.getColumnWidth(2), 0); + assertNotNull(tbl.getTableStyle()); for(XSLFTableRow row : tbl){ // all rows have the same height @@ -211,7 +210,7 @@ class TestXSLFTable { assertNotNull(tbl.getCTTable()); assertNotNull(tbl.getCTTable().getTblGrid()); assertNotNull(tbl.getCTTable().getTblPr()); - assertTrue(tbl.getXmlObject() instanceof CTGraphicalObjectFrame); + assertInstanceOf(CTGraphicalObjectFrame.class, tbl.getXmlObject()); assertEquals("Table 2", tbl.getShapeName()); assertEquals(2, tbl.getShapeId()); assertEquals(0, tbl.getRows().size()); @@ -220,6 +219,7 @@ class TestXSLFTable { assertEquals(0, tbl.getNumberOfColumns()); assertEquals(0, tbl.getNumberOfRows()); + assertNull(tbl.getTableStyle()); XSLFTableRow row0 = tbl.addRow(); assertNotNull(row0.getXmlObject()); @@ -285,6 +285,7 @@ class TestXSLFTable { XMLSlideShow ss = XSLFTestDataSamples.openSampleDocument("shapes.pptx"); XSLFSlide sl = ss.getSlides().get(0); XSLFTable tab = (XSLFTable)sl.getShapes().get(4); + assertNotNull(tab.getTableStyle()); sl.removeShape(tab); XMLSlideShow ss2 = XSLFTestDataSamples.writeOutAndReadBack(ss); @@ -309,6 +310,7 @@ class TestXSLFTable { XSLFTableCell tc0 = tr.addCell(); tc0.setText("bla bla bla bla"); tab.setColumnWidth(0, 50); + assertNull(tab.getTableStyle()); // usually text height == 88, but font rendering is platform dependent // so we use something more reliable @@ -359,7 +361,76 @@ class TestXSLFTable { new DrawTableShape(newTable).setAllBorders(3., StrokeStyle.LineDash.LG_DASH_DOT, Color.BLUE); assertEquals(3, newTable.getCTTable().getTblGrid().sizeOfGridColArray()); + assertNull(newTable.getTableStyle()); } } -} \ No newline at end of file + private void verifyTableCellStyleColors(XSLFTableCell cell, String text, Color fontColor, Color fillColor) { + assertEquals(text, cell.getText()); + PaintStyle colorText1 = cell.getTextParagraphs().get(0).getTextRuns().get(0).getFontColor(); + assertInstanceOf(PaintStyle.SolidPaint.class, colorText1); + assertEquals(fontColor, ((PaintStyle.SolidPaint)colorText1).getSolidColor().getColor()); + assertEquals(fillColor, cell.getFillColor()); + } + + @Test + void testTableCellStyle() throws IOException { + XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("table-with-different-font-colors.pptx"); + + // First slide: test row-related table styles + + List shapes = ppt.getSlides().get(0).getShapes(); + assertEquals(1, shapes.size()); + assertInstanceOf(XSLFTable.class, shapes.get(0)); + XSLFTable tbl = (XSLFTable)shapes.get(0); + assertEquals(4, tbl.getNumberOfColumns()); + assertEquals(4, tbl.getNumberOfRows()); + assertNotNull(tbl.getCTTable()); + assertNotNull(tbl.getTableStyle()); + + // Yellow font color due to "first row" table style + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(0), "Text 1", + new Color(255, 255, 0), new Color(21, 96, 130)); + // Dark green font color due to direct format + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(2), "Text 3", + new Color(0, 176, 80), new Color(21, 96, 130)); + // Grey font color due to "even row" table style + fallback + verifyTableCellStyleColors(tbl.getRows().get(1).getCells().get(0), "Text 5", + new Color(119, 119, 119), new Color(204, 210, 216)); + // Light blue font color due to "odd row" table style + verifyTableCellStyleColors(tbl.getRows().get(2).getCells().get(0), "Text 9", + new Color(0, 255, 255), new Color(231, 234, 237)); + // Red font color due to direct format + verifyTableCellStyleColors(tbl.getRows().get(2).getCells().get(2), "Text 11", + new Color(255, 0, 0), new Color(231, 234, 237)); + // Blue font color due to "last row" table style + verifyTableCellStyleColors(tbl.getRows().get(3).getCells().get(0), "Text 13", + new Color(0, 0, 255), new Color(21, 96, 130)); + + // Second slide: test column-related table styles + + shapes = ppt.getSlides().get(1).getShapes(); + assertEquals(1, shapes.size()); + assertInstanceOf(XSLFTable.class, shapes.get(0)); + tbl = (XSLFTable)shapes.get(0); + assertEquals(4, tbl.getNumberOfColumns()); + assertEquals(4, tbl.getNumberOfRows()); + assertNotNull(tbl.getCTTable()); + assertNotNull(tbl.getTableStyle()); + + // Green font color due to "first column" table style + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(0), "Text 1", + new Color(0, 255, 0), new Color(21, 96, 130)); + // Grey font color due to "even column" table style + fallback + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(1), "Text 2", + new Color(119, 119, 119), new Color(204, 210, 216)); + // Light blue font color due to "odd column" table style + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(2), "Text 3", + new Color(0, 255, 255), new Color(231, 234, 237)); + // Red font color due to "last column" table style + verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(3), "Text 4", + new Color(255, 0, 0), new Color(21, 96, 130)); + + ppt.close(); + } +} diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java index 397db98d12..171b33c5ed 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -982,4 +982,4 @@ class TestXSLFTextShape { assertEquals(textExp, textAct); } } -} \ No newline at end of file +} diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java index 8fbd2ad535..dff645af4c 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java @@ -677,6 +677,20 @@ public final class TestXSSFExportToXML { } } + @Test + void testMissingRow() throws Exception { + try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("xxe_in_schema.xlsx")) { + // delete the row to cause a null-row + wb.getSheetAt(0).removeRow(wb.getSheetAt(0).getRow(9)); + + for (XSSFMap map : wb.getCustomXMLMappings()) { + XSSFExportToXml exporter = new XSSFExportToXml(map); + UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get(); + assertThrows(SAXParseException.class, () -> exporter.exportToXML(bos, true)); + } + } + } + private static class XPathNSContext implements NamespaceContext { final Map nsMap = new HashMap<>(); diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestOutOfOrderColumns.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestOutOfOrderColumns.java index b7b6e67030..d53de4fe20 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestOutOfOrderColumns.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestOutOfOrderColumns.java @@ -1,122 +1,122 @@ -/* - * ==================================================================== - * 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.poi.xssf.streaming; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.junit.jupiter.api.Test; - -/** - * Test creates cells in reverse column order in XSSF and SXSSF and expects - * saved files to have fixed the order. - * - * This is necessary because if columns in the saved file are out of order - * Excel will show a repair dialog when opening the file and removing data. - */ -public final class TestOutOfOrderColumns { - - @Test - void outOfOrderColumnsXSSF() throws IOException { - try ( - XSSFWorkbook wb = new XSSFWorkbook(); - UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() - ) { - XSSFSheet sheet = wb.createSheet(); - - Row row = sheet.createRow(0); - // create cells in reverse order - row.createCell(1).setCellValue("def"); - row.createCell(0).setCellValue("abc"); - - wb.write(bos); - - validateOrder(bos.toInputStream()); - } - } - - @Test - void outOfOrderColumnsSXSSF() throws IOException { - try ( - SXSSFWorkbook wb = new SXSSFWorkbook(); - UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() - ) { - Sheet sheet = wb.createSheet(); - - Row row = sheet.createRow(0); - // create cells in reverse order - row.createCell(1).setCellValue("xyz"); - row.createCell(0).setCellValue("uvw"); - - wb.write(bos); - - validateOrder(bos.toInputStream()); - } - } - - @Test - /** this is the problematic case, as XSSF column sorting is skipped when saving with SXSSF. */ - void mixOfXSSFAndSXSSF() throws IOException { - try ( - XSSFWorkbook wb = new XSSFWorkbook(); - UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() - ) { - XSSFSheet sheet = wb.createSheet(); - - Row row1 = sheet.createRow(0); - // create cells in reverse order - row1.createCell(1).setCellValue("def"); - row1.createCell(0).setCellValue("abc"); - - try (SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(wb)) { - Sheet sSheet = sxssfWorkbook.getSheetAt(0); - Row row2 = sSheet.createRow(1); - // create cells in reverse order - row2.createCell(1).setCellValue("xyz"); - row2.createCell(0).setCellValue("uvw"); - - sxssfWorkbook.write(bos); - - validateOrder(bos.toInputStream()); - } - } - } - - private void validateOrder(InputStream is) throws IOException { - // test if saved cells are in order - try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is)) { - XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); - - Row resultRow = xssfSheet.getRow(0); - // POI doesn't show stored order because _cells TreeMap sorts it automatically. - // The only way to test is to compare the xml. - String s = resultRow.toString(); - assertTrue(s.matches("(?s).*A1.*B1.*"), "unexpected order: " + s); - } - } - -} +/* + * ==================================================================== + * 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.poi.xssf.streaming; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; + +/** + * Test creates cells in reverse column order in XSSF and SXSSF and expects + * saved files to have fixed the order. + * + * This is necessary because if columns in the saved file are out of order + * Excel will show a repair dialog when opening the file and removing data. + */ +public final class TestOutOfOrderColumns { + + @Test + void outOfOrderColumnsXSSF() throws IOException { + try ( + XSSFWorkbook wb = new XSSFWorkbook(); + UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() + ) { + XSSFSheet sheet = wb.createSheet(); + + Row row = sheet.createRow(0); + // create cells in reverse order + row.createCell(1).setCellValue("def"); + row.createCell(0).setCellValue("abc"); + + wb.write(bos); + + validateOrder(bos.toInputStream()); + } + } + + @Test + void outOfOrderColumnsSXSSF() throws IOException { + try ( + SXSSFWorkbook wb = new SXSSFWorkbook(); + UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() + ) { + Sheet sheet = wb.createSheet(); + + Row row = sheet.createRow(0); + // create cells in reverse order + row.createCell(1).setCellValue("xyz"); + row.createCell(0).setCellValue("uvw"); + + wb.write(bos); + + validateOrder(bos.toInputStream()); + } + } + + @Test + /** this is the problematic case, as XSSF column sorting is skipped when saving with SXSSF. */ + void mixOfXSSFAndSXSSF() throws IOException { + try ( + XSSFWorkbook wb = new XSSFWorkbook(); + UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get() + ) { + XSSFSheet sheet = wb.createSheet(); + + Row row1 = sheet.createRow(0); + // create cells in reverse order + row1.createCell(1).setCellValue("def"); + row1.createCell(0).setCellValue("abc"); + + try (SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(wb)) { + Sheet sSheet = sxssfWorkbook.getSheetAt(0); + Row row2 = sSheet.createRow(1); + // create cells in reverse order + row2.createCell(1).setCellValue("xyz"); + row2.createCell(0).setCellValue("uvw"); + + sxssfWorkbook.write(bos); + + validateOrder(bos.toInputStream()); + } + } + } + + private void validateOrder(InputStream is) throws IOException { + // test if saved cells are in order + try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is)) { + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + + Row resultRow = xssfSheet.getRow(0); + // POI doesn't show stored order because _cells TreeMap sorts it automatically. + // The only way to test is to compare the xml. + String s = resultRow.toString(); + assertTrue(s.matches("(?s).*A1.*B1.*"), "unexpected order: " + s); + } + } + +} diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFCell.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFCell.java index 742b29af49..4e750fd82a 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFCell.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFCell.java @@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import java.time.LocalDateTime; import java.util.List; import org.apache.poi.common.usermodel.HyperlinkType; @@ -61,6 +62,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; @@ -762,4 +764,89 @@ public final class TestXSSFCell extends BaseTestXCell { Cell cell = new XSSFWorkbook().createSheet().createRow(0).createCell(0); assertThrows(IllegalStateException.class, cell::getErrorCellValue); } + + @Test + void setStringToBlank() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + cell.setCellValue("test123"); + cell.setCellType(CellType.BLANK); + assertEquals("", cell.getStringCellValue()); + } + } + + @Test + void setInlineStringToNewStringValue() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + setInlineString(cell, "text123"); + cell.setCellValue("newValue"); + assertEquals("newValue", cell.getStringCellValue()); + assertFalse(cell.getCTCell().isSetIs(), "cell has InlineString XML struct still?"); + } + } + + @Test + void setInlineStringToNewNumberValue() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + setInlineString(cell, "text123"); + cell.setCellValue(123.456d); + assertEquals(123.456d, cell.getNumericCellValue()); + assertFalse(cell.getCTCell().isSetIs(), "cell has InlineString XML struct still?"); + } + } + + @Test + void setInlineStringToNewLocalDateValue() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + setInlineString(cell, "text123"); + LocalDateTime ldt = LocalDateTime.parse("2025-12-18T19:01:34"); + cell.setCellValue(ldt); + assertEquals(ldt, cell.getLocalDateTimeCellValue()); + assertFalse(cell.getCTCell().isSetIs(), "cell has InlineString XML struct still?"); + } + } + + @Test + void setInlineStringToBlank() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + setInlineString(cell, "text123"); + cell.setCellType(CellType.BLANK); + assertEquals("", cell.getStringCellValue()); + assertFalse(cell.getCTCell().isSetIs(), "cell has InlineString XML struct still?"); + } + } + + @Test + void setFormulaToBlank() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet(); + XSSFCell cell = sheet.createRow(0).createCell(0); + cell.setCellFormula("ISODD(1)"); + cell.setCellValue("3.14"); + assertTrue(cell.getCTCell().isSetF(), "cell has formula XML struct?"); + assertEquals("ISODD(1)", cell.getCellFormula()); + assertEquals("3.14", cell.getStringCellValue()); + cell.setCellType(CellType.BLANK); + assertEquals("", cell.getStringCellValue()); + assertFalse(cell.getCTCell().isSetF(), "cell has formula XML struct still?"); + } + } + + private static void setInlineString(XSSFCell cell, String text) { + cell.setCellType(CellType.STRING); + CTRst rst = CTRst.Factory.newInstance(); + rst.setT(text); + cell.getCTCell().setT(STCellType.INLINE_STR); + cell.getCTCell().setIs(rst); + assertEquals(text, cell.getStringCellValue()); + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFTable.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFTable.java index 8cdf4706f3..5fb20b6152 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFTable.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFTable.java @@ -39,7 +39,6 @@ import org.junit.jupiter.api.Test; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyleInfo; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; import static org.junit.jupiter.api.Assertions.*; @@ -784,4 +783,19 @@ public final class TestXSSFTable { } } } + + @Test + void testGetStyleNameNull() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sh = wb.createSheet(); + AreaReference tableArea = new AreaReference("B2:B6", wb.getSpreadsheetVersion()); + XSSFTable table = sh.createTable(tableArea); + + assertNull(table.getStyleName()); + + table.getCTTable().setTableStyleInfo(CTTableStyleInfo.Factory.newInstance()); + + assertNull(table.getStyleName()); + } + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java index 95ccbb3c8a..2c92887a0a 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java @@ -252,7 +252,7 @@ public final class TestColumnHelper { } /** - * Creates and adds a hidden column and then a best fit column with the given min/max pairs. + * Creates and adds a hidden column and then the best fit column with the given min/max pairs. * Suitable for testing handling of overlap. */ private static CTCols createHiddenAndBestFitColsWithHelper(int hiddenMin, int hiddenMax, int bestFitMin, int bestFitMax) { @@ -404,7 +404,6 @@ public final class TestColumnHelper { return count; } - @SuppressWarnings("deprecation") @Test void testColumnsCollapsed() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java index 36c30a7428..cc0349c283 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFDocument.java @@ -44,6 +44,7 @@ import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xwpf.XWPFTestDataSamples; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.xmlbeans.XmlCursor; @@ -512,11 +513,22 @@ public final class TestXWPFDocument { try (XWPFDocument doc = new XWPFDocument( POIDataSamples.getDocumentInstance().openResourceAsStream("unicode-path.docx"))) { // expect exception here + + assertNotNull(doc); } }); assertEquals("InvalidFormatException", ex.getCause().getClass().getSimpleName()); } + @Test + void testChartExIgnored() throws IOException { + // see https://github.com/apache/poi/pull/982 + try (XWPFDocument doc = new XWPFDocument( + POIDataSamples.getDocumentInstance().openResourceAsStream("chartex.docx"))) { + assertNotNull(doc); + } + } + @Test @Disabled("XWPF should be able to write to a new Stream when opened Read-Only") void testWriteFromReadOnlyOPC() throws Exception { @@ -535,4 +547,13 @@ public final class TestXWPFDocument { } } } + + @Test + void testFileHandleLeak() { + //noinspection resource + assertThrows(POIXMLException.class, + // Use XSSFWorkbook on purpose here to trigger missing closing of file-handle + () -> new XSSFWorkbook( + POIDataSamples.getDocumentInstance().getFile("clusterfuzz-testcase-minimized-POIXWPFFuzzer-4791943399604224.docx"))); + } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java index 235c17d3ab..9b40eb4837 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java @@ -95,6 +95,29 @@ public final class TestXWPFSDT { } } + @Test + void testGetSDTContentBodyElements() throws Exception { + try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx")) { + IBodyElement sdtBodyElement = doc.getBodyElements().get(2); + assertTrue(sdtBodyElement instanceof XWPFSDT, "sdtBodyElement instance of XWPFSDT"); + XWPFSDTContent content = (XWPFSDTContent) ((XWPFSDT) sdtBodyElement).getContent(); + assertEquals(3, content.getBodyElements().size(), "elements inside SDT"); + + ISDTContents c1 = content.getBodyElements().get(0); + assertTrue(c1 instanceof XWPFParagraph, "c1 instance of XWPFParagraph"); + assertEquals("Rich_text_pre_table", ((XWPFParagraph) c1).getText()); + + ISDTContents c2 = content.getBodyElements().get(1); + assertTrue(c2 instanceof XWPFTable, "c2 instance of XWPFTable"); + assertEquals(3, ((XWPFTable) c2).getNumberOfRows(), "rows in table inside SDT"); + assertEquals("Rich_text_cell1", ((XWPFTable) c2).getRow(0).getCell(0).getText()); + + ISDTContents c3 = content.getBodyElements().get(2); + assertTrue(c3 instanceof XWPFParagraph, "c3 instance of XWPFParagraph"); + assertEquals("Rich_text_post_table", ((XWPFParagraph) c3).getText()); + } + } + /** * POI-54771 and TIKA-1317 */ diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java index 0ae73cfd23..38c9fb949e 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTheme.java @@ -21,11 +21,13 @@ import org.apache.poi.xslf.usermodel.XSLFColor; import org.apache.poi.xwpf.XWPFTestDataSamples; import org.junit.jupiter.api.Test; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; import java.awt.*; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public final class TestXWPFTheme { @@ -36,6 +38,11 @@ public final class TestXWPFTheme { assertEquals("Office Theme", theme.getName()); assertEquals("Cambria", theme.getMajorFont()); assertEquals("Calibri", theme.getMinorFont()); + assertEquals("Angsana New", theme.getMajorFontForScript("Thai")); + assertEquals("Cordia New", theme.getMinorFontForScript("Thai")); + CTOfficeStyleSheet styleSheet = theme.getCTOfficeStyleSheet(); + assertNotNull(styleSheet); + assertEquals("Office", styleSheet.getThemeElements().getFontScheme().getName()); CTColor accent1 = theme.getCTColor("accent1"); XSLFColor color = new XSLFColor(accent1, null, null, null); assertEquals(new Color(79, 129, 189), color.getColor()); diff --git a/poi-ooxml/src/test/java9/module-info.java b/poi-ooxml/src/test/java9/module-info.java index ab8306e8a1..bd058c8243 100644 --- a/poi-ooxml/src/test/java9/module-info.java +++ b/poi-ooxml/src/test/java9/module-info.java @@ -200,6 +200,7 @@ module org.apache.poi.ooxml { opens org.apache.poi.xssf.streaming to org.junit.platform.commons; opens org.apache.poi.xssf.util to org.junit.platform.commons; opens org.apache.poi.xslf.draw to org.junit.platform.commons; + opens org.apache.poi.xslf.draw.geom to org.junit.platform.commons; opens org.apache.poi.xslf.usermodel to org.junit.platform.commons; opens org.apache.poi.xslf.model to org.junit.platform.commons; opens org.apache.poi.xslf.util to org.junit.platform.commons; diff --git a/poi-scratchpad/build.gradle b/poi-scratchpad/build.gradle index fd37a9922f..e67aed79ae 100644 --- a/poi-scratchpad/build.gradle +++ b/poi-scratchpad/build.gradle @@ -88,7 +88,7 @@ tasks.register('compileTest9', JavaCompile) { jar { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}") manifest { attributes('Automatic-Module-Name': MODULE_NAME, 'Multi-Release': 'true') @@ -98,7 +98,7 @@ jar { // Create a separate jar for test-code to depend on it in other projects // See http://stackoverflow.com/questions/5144325/gradle-test-dependency task testJar(type: Jar, dependsOn: testClasses) { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-tests") setArchiveClassifier 'tests' // ignore second module-info.class from main diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java index 5918158564..daa9bb4bc6 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java @@ -39,10 +39,14 @@ import org.apache.poi.hwmf.record.HwmfDraw; import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject; import org.apache.poi.util.GenericRecordJsonWriter; import org.apache.poi.util.GenericRecordUtil; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianInputStream; public final class HemfDraw { + // arbitrary limit to avoid OOM on malformed files. This may need increasing if "normal" files have more than this + public static final int MAX_NUMBER_OF_POLYGONS = 100_000; + private HemfDraw() {} /** @@ -501,6 +505,7 @@ public final class HemfDraw { size += 2 * LittleEndianConsts.INT_SIZE; // An array of 32-bit unsigned integers that specifies the point count for each polygon. + IOUtils.safelyAllocateCheck(numberOfPolygons, MAX_NUMBER_OF_POLYGONS); long[] polygonPointCount = new long[(int)numberOfPolygons]; size += numberOfPolygons * LittleEndianConsts.INT_SIZE; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java index 32215fd81e..e293860a59 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.util.Map; import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.poi.common.usermodel.GenericRecord; import org.apache.poi.hemf.draw.HemfGraphics; import org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState; @@ -38,6 +40,8 @@ import org.apache.poi.util.LittleEndianInputStream; @Internal public class HemfPlusHeader implements HemfPlusRecord { + private static final Logger log = LogManager.getLogger(HemfPlusHeader.class); + /** * The GraphicsVersion enumeration defines versions of operating system graphics that are used to * create EMF+ metafiles. @@ -89,7 +93,12 @@ public class HemfPlusHeader implements HemfPlusRecord { this.flags = flags; version.init(leis); - assert(version.getMetafileSignature() == 0xDBC01 && version.getGraphicsVersion() != null); + if (version.getMetafileSignature() != 0xDBC01) { + log.atWarn().log("Had invalid meta-file signature, expected " + 0xDBC01 + ", had: " + version.getMetafileSignature()); + } + if (version.getGraphicsVersion() == null) { + log.atWarn().log("Encountered empty graphics version in emf file"); + } emfPlusFlags = leis.readUInt(); diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PPDrawing.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PPDrawing.java index 267034076a..886468362e 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PPDrawing.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PPDrawing.java @@ -47,6 +47,7 @@ import org.apache.poi.ddf.EscherSpgrRecord; import org.apache.poi.ddf.EscherTextboxRecord; import org.apache.poi.sl.usermodel.ShapeType; import org.apache.poi.util.GenericRecordUtil; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndian; /** @@ -65,6 +66,7 @@ import org.apache.poi.util.LittleEndian; // would require a wrapping class public final class PPDrawing extends RecordAtom implements Iterable { + public static final int MAX_RECORD_SIZE = 20_000_000; private final byte[] _header; private long _type; @@ -214,8 +216,9 @@ public final class PPDrawing extends RecordAtom implements Iterable s.getShapeId() == masterId).findFirst().orElse(null); + HSLFMasterSheet masterSheet = shape.getSheet().getMasterSheet(); + if (masterSheet == null) { + return null; + } + + HSLFShape o = masterSheet.getShapes().stream().filter(s -> s.getShapeId() == masterId).findFirst().orElse(null); return o != null ? o.getFillStyle().getPaint() : null; } @@ -452,7 +458,6 @@ public final class HSLFFill { } } - @SuppressWarnings("resource") EscherBSERecord getEscherBSERecord(int idx){ HSLFSheet sheet = shape.getSheet(); if(sheet == null) { @@ -555,11 +560,10 @@ public final class HSLFFill { /** * {@code PictureData} object used in a texture, pattern of picture fill. */ - @SuppressWarnings("resource") public HSLFPictureData getPictureData(){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); - EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__PATTERNTEXTURE); - if (p == null) { + EscherProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__PATTERNTEXTURE); + if (!(p instanceof EscherSimpleProperty)) { return null; } @@ -570,7 +574,7 @@ public final class HSLFFill { EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); EscherContainerRecord bstore = HSLFShape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); - int idx = p.getPropertyValue(); + int idx = ((EscherSimpleProperty)p).getPropertyValue(); if (idx == 0){ LOG.atWarn().log("no reference to picture data found "); } else { diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFGroupShape.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFGroupShape.java index 1754e67d2b..66edf97706 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFGroupShape.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFGroupShape.java @@ -78,7 +78,7 @@ implements HSLFShapeContainer, GroupShape { public void setAnchor(Rectangle2D anchor) { EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID); boolean isInitialized = !(clientAnchor.getDx1() == 0 && clientAnchor.getRow1() == 0); - + if (isInitialized) { moveAndScale(anchor); } else { @@ -113,7 +113,7 @@ implements HSLFShapeContainer, GroupShape { protected void setExteriorAnchor(Rectangle2D anchor) { EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID); - + //hack. internal variable EscherClientAnchorRecord.shortRecord can be //initialized only in fillFields(). We need to set shortRecord=false; byte[] header = new byte[16]; @@ -131,7 +131,7 @@ implements HSLFShapeContainer, GroupShape { // TODO: does this make sense? setInteriorAnchor(anchor); } - + /** * Create a new ShapeGroup and create an instance of EscherSpgrContainer which represents a group of shapes */ @@ -186,7 +186,7 @@ implements HSLFShapeContainer, GroupShape { double scaleY = (anchorSrc.getHeight() == 0) ? 0 : anchorDest.getHeight() / anchorSrc.getHeight(); setExteriorAnchor(anchorDest); - + for (HSLFShape shape : getShapes()) { Rectangle2D chanchor = shape.getAnchor(); double x = anchorDest.getX()+(chanchor.getX()-anchorSrc.getX())*scaleX; @@ -204,12 +204,16 @@ implements HSLFShapeContainer, GroupShape { * @return the anchor of this shape group */ @Override - public Rectangle2D getAnchor(){ + public Rectangle2D getAnchor() { EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID); int x1,y1,x2,y2; - if(clientAnchor == null){ + if(clientAnchor == null) { LOG.atInfo().log("EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord."); EscherChildAnchorRecord rec = getEscherChild(EscherChildAnchorRecord.RECORD_ID); + if (rec == null) { + throw new IllegalStateException("Cannot get anchor, neither had an EscherClientAnchorRecord nor an EscherChildAnchorRecord"); + } + x1 = rec.getDx1(); y1 = rec.getDy1(); x2 = rec.getDx2(); @@ -293,7 +297,7 @@ implements HSLFShapeContainer, GroupShape { isFirst = false; continue; } - + if(r instanceof EscherContainerRecord) { // Create the Shape for it EscherContainerRecord container = (EscherContainerRecord)r; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFPictureShape.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFPictureShape.java index e10a54cb55..a311dbf40d 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFPictureShape.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFPictureShape.java @@ -228,4 +228,9 @@ public class HSLFPictureShape extends HSLFSimpleShape implements PictureShape { * @return escher property or {@code null} if not found. */ public static T getEscherProperty(AbstractEscherOptRecord opt, EscherPropertyTypes type){ - return (opt == null) ? null : opt.lookup(type); + return opt == null ? null : opt.lookup(type); } /** @@ -408,11 +408,13 @@ public abstract class HSLFShape implements Shape { if (fSchemeIndex && sheet != null) { //red is the index to the color scheme ColorSchemeAtom ca = sheet.getColorScheme(); - int schemeColor = ca.getColor(ecr.getSchemeIndex()); + if (ca != null) { + int schemeColor = ca.getColor(ecr.getSchemeIndex()); - rgb[0] = (schemeColor >> 0) & 0xFF; - rgb[1] = (schemeColor >> 8) & 0xFF; - rgb[2] = (schemeColor >> 16) & 0xFF; + rgb[0] = (schemeColor >> 0) & 0xFF; + rgb[1] = (schemeColor >> 8) & 0xFF; + rgb[2] = (schemeColor >> 16) & 0xFF; + } } else if (fPaletteIndex) { //TODO } else if (fPaletteRGB) { @@ -657,9 +659,9 @@ public abstract class HSLFShape implements Shape { } /** - * Search for EscherClientDataRecord, if found, convert its contents into an array of HSLF records + * Search for EscherClientDataRecord, if found, convert its contents into a list of HSLF records * - * @return an array of HSLF records contained in the shape's EscherClientDataRecord or {@code null} + * @return a list of HSLF records contained in the shape's EscherClientDataRecord or {@code null} */ protected List getClientRecords() { HSLFEscherClientDataRecord clientData = getClientData(false); diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java index 65dcf6dda5..b0929d3bb7 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java @@ -112,6 +112,10 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { return null; } final TxMasterStyleAtom t = _txmaster[txtype]; + if (t == null) { + throw new IllegalStateException("Cannot get master-style for type " + txtype); + } + final List styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles(); // TODO: what is the reaction for readOnly=false and styles.isEmpty()? final int minLevel = Math.min(level, styles.size()-1); diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index 4e5e3280d2..c812f0df6f 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -714,7 +714,10 @@ public final class HSLFTextParagraph implements TextParagraph { return -1; } List paras = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); - return (paras.isEmpty() || paras.get(0).getIndex() == -1) ? -1 : paras.get(0).getRunType(); + return (paras == null || paras.isEmpty() || paras.get(0).getIndex() == -1) ? -1 : paras.get(0).getRunType(); } /** diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfBitmapDib.java b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfBitmapDib.java index b6c59a2dfe..cfe5804ee3 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfBitmapDib.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfBitmapDib.java @@ -51,6 +51,9 @@ import org.apache.poi.util.RecordFormatException; * The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format. */ public class HwmfBitmapDib implements GenericRecord { + // arbitrarily selected; may need to increase + private static final int DEFAULT_MAX_HEIGHT_WIDTH = 10_000; + protected static int MAX_HEIGHT_WIDTH = DEFAULT_MAX_HEIGHT_WIDTH; private static final Logger LOG = PoiLogManager.getLogger(HwmfBitmapDib.class); private static final int BMP_HEADER_SIZE = 14; @@ -243,7 +246,8 @@ public class HwmfBitmapDib implements GenericRecord { // The size and format of this data is determined by information in the DIBHeaderInfo field. If // it is a BitmapCoreHeader, the size in bytes MUST be calculated as follows: - int bodySize = ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight)); + int bodySize = ((((headerWidth * headerPlanes * + (headerBitCount == null ? 0 : headerBitCount.flag) + 31) & ~31) / 8) * Math.abs(headerHeight)); // This formula SHOULD also be used to calculate the size of aData when DIBHeaderInfo is a // BitmapInfoHeader Object, using values from that object, but only if its Compression value is @@ -348,6 +352,10 @@ public class HwmfBitmapDib implements GenericRecord { } protected int readColors(LittleEndianInputStream leis) throws IOException { + if (headerBitCount == null) { + return 0; + } + switch (headerBitCount) { default: case BI_BITCOUNT_0: @@ -527,6 +535,13 @@ public class HwmfBitmapDib implements GenericRecord { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } + if (headerHeight > MAX_HEIGHT_WIDTH || headerWidth > MAX_HEIGHT_WIDTH) { + throw new RecordFormatException("The width or height specified in the header exceed the current " + + "limit. Height: " + headerHeight + ", width: " + headerWidth + + ", Max width/height: " + MAX_HEIGHT_WIDTH + + ". Limits can be adjusted via 'HwmfBitmapDib.setMaxHeightWidth'"); + } + BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g = bi.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -554,4 +569,21 @@ public class HwmfBitmapDib implements GenericRecord { g.dispose(); return bi; } + + /** + * Adjust limit to prevent broken images from exceeding available + * memory when being drawn. + * + * @param length the max number of pixel of width/height to allow for images + */ + public static void setMaxHeightWidth(int length) { + MAX_HEIGHT_WIDTH = length; + } + + /** + * @return the max number of pixel of width/height to allow for images + */ + public static int getMaxHeightWidth() { + return MAX_HEIGHT_WIDTH; + } } diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfEscape.java b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfEscape.java index 39586fdd7e..909d1c4f06 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfEscape.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfEscape.java @@ -23,6 +23,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.poi.common.usermodel.GenericRecord; import org.apache.poi.hwmf.draw.HwmfGraphics; import org.apache.poi.util.GenericRecordJsonWriter; @@ -37,6 +39,8 @@ import org.apache.poi.util.LittleEndianInputStream; */ @SuppressWarnings("WeakerAccess") public class HwmfEscape implements HwmfRecord { + private static final Logger log = LogManager.getLogger(HwmfEscape.class); + private static final int MAX_OBJECT_SIZE = 0xFFFF; public enum EscapeFunction { @@ -307,7 +311,9 @@ public class HwmfEscape implements HwmfRecord { // A 32-bit unsigned integer that identifies the type of comment in this record. // This value MUST be 0x00000001. commentType = leis.readInt(); - assert(commentType == 0x00000001); + if (commentType != 0x00000001) { + HwmfEscape.log.atWarn().log("Unexpected comment-type: {}", commentType); + } // A 32-bit unsigned integer that specifies EMF metafile interoperability. This SHOULD be 0x00010000. version = leis.readInt(); diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfMisc.java b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfMisc.java index da669354df..8130f7d7e3 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwmf/record/HwmfMisc.java @@ -511,8 +511,18 @@ public class HwmfMisc { @Override public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { - style = HwmfBrushStyle.valueOf(leis.readUShort()); - colorUsage = ColorUsage.valueOf(leis.readUShort()); + int brushStyle = leis.readUShort(); + style = HwmfBrushStyle.valueOf(brushStyle); + if (style == null) { + throw new IllegalArgumentException("Could not read brush-style " + brushStyle); + } + + int colorUsageEnum = leis.readUShort(); + colorUsage = ColorUsage.valueOf(colorUsageEnum); + if (colorUsage == null) { + throw new IllegalArgumentException("Could not read color-usage " + colorUsage); + } + int size = 2*LittleEndianConsts.SHORT_SIZE; switch (style) { case BS_SOLID: diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hmef/TestHMEFMessage.java b/poi-scratchpad/src/test/java/org/apache/poi/hmef/TestHMEFMessage.java index 5a12ac6426..e40aef9b1e 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hmef/TestHMEFMessage.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hmef/TestHMEFMessage.java @@ -237,9 +237,8 @@ public final class TestHMEFMessage { static void assertContents(String filename, byte[] actual) throws IOException { try (InputStream stream = _samples.openResourceAsStream("quick-contents/" + filename)) { byte[] expected = IOUtils.toByteArray(stream); - assertArrayEquals(expected, actual); + assertArrayEquals(expected, actual, "Data comparison failed for " + filename); } } } - diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestPPTXMLDump.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestPPTXMLDump.java index 213b05387c..7c2ec525c7 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestPPTXMLDump.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestPPTXMLDump.java @@ -37,6 +37,7 @@ public class TestPPTXMLDump extends BaseTestPPTIterating { LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6360479850954752.ppt"); LOCAL_EXCLUDED.add("ppt_with_png_encrypted.ppt"); LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6614960949821440.ppt"); + LOCAL_EXCLUDED.add("60f557c0a46bcb0068b1c3e15589dac383307bc8.ppt"); } @Test diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideIdListing.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideIdListing.java index f86247c86b..684f4e8e42 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideIdListing.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideIdListing.java @@ -32,6 +32,7 @@ public class TestSlideIdListing extends BaseTestPPTIterating { static { LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-5306877435838464.ppt"); LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6360479850954752.ppt"); + LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt"); } @Test diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowDumper.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowDumper.java index 290616ce10..1c97bba430 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowDumper.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowDumper.java @@ -39,6 +39,7 @@ public class TestSlideShowDumper extends BaseTestPPTIterating { FAILING.add("41384.ppt"); FAILING.add("bug56240.ppt"); FAILING.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6360479850954752.ppt"); + FAILING.add("60f557c0a46bcb0068b1c3e15589dac383307bc8.ppt"); } static final Set LOCAL_EXCLUDED = new HashSet<>(); diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowRecordDumper.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowRecordDumper.java index 73bbd376fd..f5705fe981 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowRecordDumper.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestSlideShowRecordDumper.java @@ -25,12 +25,14 @@ import java.util.Set; import org.apache.poi.EmptyFileException; import org.apache.poi.hslf.HSLFTestDataSamples; +import org.apache.poi.util.RecordFormatException; import org.junit.jupiter.api.Test; public class TestSlideShowRecordDumper extends BaseTestPPTIterating { static final Set LOCAL_EXCLUDED = new HashSet<>(); static { LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6360479850954752.ppt"); + LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt"); } @Test @@ -58,7 +60,7 @@ public class TestSlideShowRecordDumper extends BaseTestPPTIterating { void runOneFile(File pFile) throws Exception { try { SlideShowRecordDumper.main(new String[]{pFile.getAbsolutePath()}); - } catch (IllegalStateException e) { + } catch (IllegalStateException | RecordFormatException e) { if (!LOCAL_EXCLUDED.contains(pFile.getName())) { throw e; } diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestUserEditAndPersistListing.java b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestUserEditAndPersistListing.java index fab8679776..e639e292b0 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestUserEditAndPersistListing.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hslf/dev/TestUserEditAndPersistListing.java @@ -24,12 +24,14 @@ import java.util.HashSet; import java.util.Set; import org.apache.poi.EmptyFileException; +import org.apache.poi.util.RecordFormatException; import org.junit.jupiter.api.Test; public class TestUserEditAndPersistListing extends BaseTestPPTIterating { static final Set LOCAL_EXCLUDED = new HashSet<>(); static { LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6360479850954752.ppt"); + LOCAL_EXCLUDED.add("clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt"); } @Test @@ -42,7 +44,7 @@ public class TestUserEditAndPersistListing extends BaseTestPPTIterating { void runOneFile(File pFile) throws Exception { try { UserEditAndPersistListing.main(new String[]{pFile.getAbsolutePath()}); - } catch (IllegalStateException e) { + } catch (IllegalStateException | RecordFormatException e) { if (!LOCAL_EXCLUDED.contains(pFile.getName())) { throw e; } diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwmf/TestHwmfParsing.java b/poi-scratchpad/src/test/java/org/apache/poi/hwmf/TestHwmfParsing.java index dff936b099..3b31174db3 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hwmf/TestHwmfParsing.java @@ -19,8 +19,10 @@ package org.apache.poi.hwmf; import static org.apache.poi.POITestCase.assertContains; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -35,6 +37,7 @@ import org.apache.poi.hwmf.record.HwmfRecord; import org.apache.poi.hwmf.record.HwmfRecordType; import org.apache.poi.hwmf.record.HwmfText; import org.apache.poi.hwmf.usermodel.HwmfPicture; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.RecordFormatException; import org.junit.jupiter.api.Disabled; @@ -42,6 +45,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +@SuppressWarnings("UnnecessaryUnicodeEscape") public class TestHwmfParsing { private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); @@ -54,7 +58,8 @@ public class TestHwmfParsing { @CsvSource({ "santa.wmf, 581", /* Bug 65063 */ - "empty-polygon-close.wmf, 272" + "empty-polygon-close.wmf, 272", + "file-45.wmf, 1315" }) void parse(String file, int recordCnt) throws IOException { try (InputStream fis = samples.openResourceAsStream(file)) { @@ -71,6 +76,20 @@ public class TestHwmfParsing { } } + @Test + void testInvalid() throws Exception { + try (InputStream is = samples.openResourceAsStream("santa.wmf")) { + byte[] bytes = IOUtils.toByteArray(is); + + // simulate an invalid commentType, it should be logged and ignored + bytes[34] = (byte)255; + bytes[35] = (byte)255; + + HwmfPicture wmf = new HwmfPicture(new ByteArrayInputStream(bytes)); + List records = wmf.getRecords(); + assertEquals(581, records.size()); + } + } @Test @Disabled("If we decide we can use common crawl file specified, we can turn this back on") @@ -91,6 +110,8 @@ public class TestHwmfParsing { charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset(); } if (r.getWmfRecordType().equals(HwmfRecordType.extTextOut)) { + assertInstanceOf(HwmfText.WmfExtTextOut.class, r); + HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r; sb.append(textOut.getText(charset)).append("\n"); } @@ -103,7 +124,7 @@ public class TestHwmfParsing { @Test void testShift_JIS() throws Exception { //this file derives from common crawl: see Bug 60677 - HwmfPicture wmf = null; + final HwmfPicture wmf; try (InputStream fis = samples.openResourceAsStream("60677.wmf")) { wmf = new HwmfPicture(fis); } @@ -119,6 +140,8 @@ public class TestHwmfParsing { charset = (font.getCharset().getCharset() == null) ? LocaleUtil.CHARSET_1252 : font.getCharset().getCharset(); } if (r.getWmfRecordType().equals(HwmfRecordType.extTextOut)) { + assertInstanceOf(HwmfText.WmfExtTextOut.class, r); + HwmfText.WmfExtTextOut textOut = (HwmfText.WmfExtTextOut)r; sb.append(textOut.getText(charset)).append("\n"); } @@ -128,11 +151,11 @@ public class TestHwmfParsing { } @Test - void testLengths() throws Exception { + void testLengths() { //both substring and length rely on char, not codepoints. //This test confirms that the substring calls in HwmfText //will not truncate even beyond-bmp data. - //The last character (Deseret AY U+1040C) is comprised of 2 utf16 surrogates/codepoints + //The last character (Deseret AY U+1040C) consists of 2 utf16 surrogates/codepoints String s = "\u666E\u6797\u65AF\uD801\uDC0C"; Charset utf16LE = StandardCharsets.UTF_16LE; byte[] bytes = s.getBytes(utf16LE); diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java index 9cfb4b6d11..56665c679d 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java @@ -61,7 +61,8 @@ public class TestWordToConverterSuite { "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc", - "clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc" + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc" ); public static Stream files() { diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java index 9e66096b3c..c2bd0582ba 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java @@ -54,7 +54,8 @@ public class TestWordToTextConverter { "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc", - "clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc" + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-6610789829836800.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc" ); /** diff --git a/poi/build.gradle b/poi/build.gradle index 7fd4b5f544..d11aaf9ac6 100644 --- a/poi/build.gradle +++ b/poi/build.gradle @@ -124,7 +124,7 @@ jar { // Create a separate jar for test-code to depend on it in other projects // See http://stackoverflow.com/questions/5144325/gradle-test-dependency task testJar(type: Jar, dependsOn: [ testClasses, compileTest9 ]) { - destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") + destinationDirectory = file("../build/dist/maven/${base.archivesName.get()}-tests") setArchiveClassifier 'tests' // ignore second module-info.class from main @@ -138,11 +138,11 @@ task testJar(type: Jar, dependsOn: [ testClasses, compileTest9 ]) { } javadoc { - dependsOn configurations.javadocs.dependencies.collect{ ':' + it.dependencyProject.name + ':compileJava' } + dependsOn configurations.javadocs.dependencies.collect{ ':' + project.project(it.path).name + ':compileJava' } doFirst { options { - classpath += files(configurations.javadocs.dependencies.collect{ it.dependencyProject.sourceSets.main.output.classesDirs }) + classpath += files(configurations.javadocs.dependencies.collect{ project.project(it.path).sourceSets.main.output.classesDirs }) } } } diff --git a/poi/src/main/java/org/apache/poi/POIDocument.java b/poi/src/main/java/org/apache/poi/POIDocument.java index 0f4239bb82..c563d64113 100644 --- a/poi/src/main/java/org/apache/poi/POIDocument.java +++ b/poi/src/main/java/org/apache/poi/POIDocument.java @@ -473,6 +473,7 @@ public abstract class POIDocument implements Closeable { * to a new POIFSFileSystem * * @param newDirectory the new directory + * @throws IOException if accessing the file-system fails. */ @Internal protected void replaceDirectory(DirectoryNode newDirectory) throws IOException { diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java b/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java index 233fa0a8bc..9aa6bf7937 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java @@ -118,7 +118,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements private void rewriteArray(int numberOfElements, boolean copyToNewLen) { int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE; - resizeComplexData(expectedArraySize, copyToNewLen ? expectedArraySize : getComplexData().length); + resizeComplexData(expectedArraySize, copyToNewLen ? expectedArraySize : getComplexSize()); } public int getNumberOfElementsInMemory() { @@ -181,7 +181,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements // the code here seems to depend on complexData already being // sized correctly via the constructor - int cdLen = getComplexData().length; + int cdLen = getComplexSize(); int arraySize = getActualSizeOfElements(sizeOfElements) * numElements; if (arraySize == cdLen) { // The stored data size in the simple block excludes the header size @@ -190,7 +190,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements } setComplexData(data, offset); } - return getComplexData().length; + return getComplexSize(); } /** @@ -202,7 +202,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements @Override public int serializeSimplePart(byte[] data, int pos) { LittleEndian.putShort(data, pos, getId()); - int recordSize = getComplexData().length; + int recordSize = getComplexSize(); if (!sizeIncludesHeaderSize) { recordSize -= 6; } diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherComplexProperty.java b/poi/src/main/java/org/apache/poi/ddf/EscherComplexProperty.java index c001707c6c..680ee9a5ae 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherComplexProperty.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherComplexProperty.java @@ -35,6 +35,7 @@ public class EscherComplexProperty extends EscherProperty { private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000_000; private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; + private int complexSize; private byte[] complexData; /** @@ -61,7 +62,15 @@ public class EscherComplexProperty extends EscherProperty { */ public EscherComplexProperty(short id, int complexSize) { super((short)(id | IS_COMPLEX)); - complexData = IOUtils.safelyAllocate(complexSize, MAX_RECORD_LENGTH); + + // lazy-allocate the data + this.complexSize = complexSize; + } + + private void ensureComplexData(int size) { + if (this.complexData == null) { + complexData = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH); + } } /** @@ -94,7 +103,7 @@ public class EscherComplexProperty extends EscherProperty { @Override public int serializeSimplePart(byte[] data, int pos) { LittleEndian.putShort(data, pos, getId()); - LittleEndian.putInt(data, pos + 2, complexData.length); + LittleEndian.putInt(data, pos + 2, complexSize); return 6; } @@ -107,8 +116,13 @@ public class EscherComplexProperty extends EscherProperty { */ @Override public int serializeComplexPart(byte[] data, int pos) { - System.arraycopy(complexData, 0, data, pos, complexData.length); - return complexData.length; + if (complexData == null) { + // initialize empty array if complexData was never allocated + Arrays.fill(data, pos, pos + complexSize, (byte)0); + } else { + System.arraycopy(complexData, 0, data, pos, complexData.length); + } + return complexSize; } /** @@ -117,9 +131,16 @@ public class EscherComplexProperty extends EscherProperty { * @return the complex bytes */ public byte[] getComplexData() { + // we need to allocate here as sometimes the array is written to + ensureComplexData(complexSize); + return complexData; } + public int getComplexSize() { + return complexSize; + } + public int setComplexData(byte[] complexData) { return setComplexData(complexData, 0); } @@ -128,25 +149,33 @@ public class EscherComplexProperty extends EscherProperty { if (complexData == null) { return 0; } else { - int copySize = Math.max(0, Math.min(this.complexData.length, complexData.length - offset)); + int copySize = Math.max(0, Math.min(complexSize, complexData.length - offset)); + ensureComplexData(copySize); System.arraycopy(complexData, offset, this.complexData, 0, copySize); return copySize; } } - - protected void resizeComplexData(int newSize) { resizeComplexData(newSize, Integer.MAX_VALUE); } protected void resizeComplexData(int newSize, int copyLen) { - if (newSize == complexData.length) { + if (newSize == complexSize) { return; } + + // no need to copy if data was not initialized yet + if (complexData == null) { + complexSize = newSize; + + return; + } + byte[] newArray = IOUtils.safelyAllocate(newSize, MAX_RECORD_LENGTH); System.arraycopy(complexData, 0, newArray, 0, Math.min(Math.min(complexData.length, copyLen),newSize)); complexData = newArray; + complexSize = newSize; } /** @@ -166,6 +195,18 @@ public class EscherComplexProperty extends EscherProperty { EscherComplexProperty escherComplexProperty = (EscherComplexProperty) o; + // not equal if size differs + if (complexSize != escherComplexProperty.complexSize) { + return false; + } + + if (complexData == null) { + // if complexData is not initialized, it is equal only if + // complexData is also uninitialized or equals an empty array + return escherComplexProperty.complexData == null || + Arrays.equals(escherComplexProperty.complexData, new byte[escherComplexProperty.complexSize]); + } + return Arrays.equals(complexData, escherComplexProperty.complexData); } @@ -176,16 +217,18 @@ public class EscherComplexProperty extends EscherProperty { */ @Override public int getPropertySize() { - return 6 + complexData.length; + return 6 + complexSize; } @Override public int hashCode() { + ensureComplexData(complexSize); return Arrays.deepHashCode(new Object[]{complexData, getId()}); } @Override public Map> getGenericProperties() { + ensureComplexData(complexSize); return GenericRecordUtil.getGenericProperties( "base", super::getGenericProperties, "data", this::getComplexData diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherContainerRecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherContainerRecord.java index a5456159be..804fa0493d 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherContainerRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherContainerRecord.java @@ -102,14 +102,7 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl while (bytesRemaining > 0 && offset < data.length) { EscherRecord child = recordFactory.createRecord(data, offset); - final int childBytesWritten; - if (child instanceof EscherContainerRecord) { - childBytesWritten = ((EscherContainerRecord)child).fillFields(data, offset, recordFactory, nesting + 1); - } else if (child instanceof UnknownEscherRecord) { - childBytesWritten = ((UnknownEscherRecord)child).fillFields(data, offset, recordFactory, nesting + 1); - } else { - childBytesWritten = child.fillFields(data, offset, recordFactory, nesting + 1); - } + final int childBytesWritten = child.fillFields(data, offset, recordFactory, nesting + 1); bytesWritten += childBytesWritten; offset += childBytesWritten; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherPropertyFactory.java b/poi/src/main/java/org/apache/poi/ddf/EscherPropertyFactory.java index eff992c952..101df04faa 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherPropertyFactory.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherPropertyFactory.java @@ -81,7 +81,7 @@ public final class EscherPropertyFactory { pos += eap.setArrayData(data, pos); } else if (p instanceof EscherComplexProperty) { EscherComplexProperty ecp = (EscherComplexProperty)p; - int cdLen = ecp.getComplexData().length; + int cdLen = ecp.getComplexSize(); int leftover = data.length - pos; if (leftover < cdLen) { throw new IllegalStateException("Could not read complex escher property, length was " + diff --git a/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java b/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java index 67b6bc6317..98434aaace 100644 --- a/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java @@ -103,7 +103,7 @@ public final class UnknownEscherRecord extends EscherRecord { bytesWritten += childBytesWritten; offset += childBytesWritten; bytesRemaining -= childBytesWritten; - getChildRecords().add( child ); + addChildRecord( child ); } return bytesWritten; } diff --git a/poi/src/main/java/org/apache/poi/extractor/ExtractorFactory.java b/poi/src/main/java/org/apache/poi/extractor/ExtractorFactory.java index 2b8ca5a450..458c4ed65c 100644 --- a/poi/src/main/java/org/apache/poi/extractor/ExtractorFactory.java +++ b/poi/src/main/java/org/apache/poi/extractor/ExtractorFactory.java @@ -47,7 +47,7 @@ import org.apache.poi.util.ThreadLocalUtil; *

Note 1 - will fail for many file formats if the POI Scratchpad jar is * not present on the runtime classpath

*

Note 2 - for text extractor creation across all formats, use - * {@link org.apache.poi.ooxml.extractor.POIXMLExtractorFactory} contained within + * org.apache.poi.ooxml.extractor.POIXMLExtractorFactory contained within * the OOXML jar.

*

Note 3 - rather than using this, for most cases you would be better * off switching to Apache Tika instead!

@@ -294,7 +294,7 @@ public final class ExtractorFactory { /** * Create the Extractor, if possible. Generally needs the Scratchpad jar. * Note that this won't check for embedded OOXML resources either, use - * {@link org.apache.poi.ooxml.extractor.POIXMLExtractorFactory} for that. + * org.apache.poi.ooxml.extractor.POIXMLExtractorFactory for that. * * @param root The {@link DirectoryNode} pointing to a document. * @@ -313,7 +313,7 @@ public final class ExtractorFactory { /** * Create the Extractor, if possible. Generally needs the Scratchpad jar. * Note that this won't check for embedded OOXML resources either, use - * {@link org.apache.poi.ooxml.extractor.POIXMLExtractorFactory} for that. + * org.apache.poi.ooxml.extractor.POIXMLExtractorFactory for that. * * @param root The {@link DirectoryNode} pointing to a document. * @param password The password that is necessary to open the file diff --git a/poi/src/main/java/org/apache/poi/hssf/model/InternalSheet.java b/poi/src/main/java/org/apache/poi/hssf/model/InternalSheet.java index 570494d830..d50dabfcbb 100644 --- a/poi/src/main/java/org/apache/poi/hssf/model/InternalSheet.java +++ b/poi/src/main/java/org/apache/poi/hssf/model/InternalSheet.java @@ -1075,6 +1075,10 @@ public final class InternalSheet { } private void setColumn(int column, Short xfStyle, Integer width, Integer level, Boolean hidden, Boolean collapsed) { + if (_columnInfos == null) { + throw new IllegalStateException("Cannot group column range with missing column-infos"); + } + _columnInfos.setColumn( column, xfStyle, width, level, hidden, collapsed ); } @@ -1087,6 +1091,9 @@ public final class InternalSheet { * if false indenting will be removed by one level. */ public void groupColumnRange(int fromColumn, int toColumn, boolean indent) { + if (_columnInfos == null) { + throw new IllegalStateException("Cannot group column range with missing column-infos"); + } // Set the level for each column _columnInfos.groupColumnRange( fromColumn, toColumn, indent); diff --git a/poi/src/main/java/org/apache/poi/hssf/model/LinkTable.java b/poi/src/main/java/org/apache/poi/hssf/model/LinkTable.java index fe5848ca53..79ec7f0b9f 100644 --- a/poi/src/main/java/org/apache/poi/hssf/model/LinkTable.java +++ b/poi/src/main/java/org/apache/poi/hssf/model/LinkTable.java @@ -18,7 +18,6 @@ package org.apache.poi.hssf.model; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -248,8 +247,9 @@ final class LinkTable { int nItems = temp.size(); if (nItems < 1) { + Class nextClass = rs.peekNextClass(); throw new IllegalStateException("Expected an EXTERNSHEET record but got (" - + rs.peekNextClass().getName() + ")"); + + (nextClass == null ? "" : nextClass.getName()) + ")"); } if (nItems == 1) { // this is the normal case. There should be just one ExternSheetRecord @@ -368,6 +368,10 @@ final class LinkTable { } public String[] getExternalBookAndSheetName(int extRefIndex) { + if (_externSheetRecord == null) { + throw new IllegalStateException("ExternSheetRecord is not set"); + } + int ebIx = _externSheetRecord.getExtbookIndexFromRefIndex(extRefIndex); SupBookRecord ebr = _externalBookBlocks[ebIx].getExternalBookRecord(); if (!ebr.isExternalReferences()) { @@ -378,11 +382,12 @@ final class LinkTable { int shIx2 = _externSheetRecord.getLastSheetIndexFromRefIndex(extRefIndex); String firstSheetName = null; String lastSheetName = null; - if (shIx1 >= 0) { - firstSheetName = ebr.getSheetNames()[shIx1]; + String[] sheetNames = ebr.getSheetNames(); + if (shIx1 >= 0 && sheetNames != null) { + firstSheetName = sheetNames[shIx1]; } - if (shIx2 >= 0) { - lastSheetName = ebr.getSheetNames()[shIx2]; + if (shIx2 >= 0 && sheetNames != null) { + lastSheetName = sheetNames[shIx2]; } if (shIx1 == shIx2) { return new String[]{ @@ -452,8 +457,12 @@ final class LinkTable { } SupBookRecord ebrTarget = _externalBookBlocks[externalBookIndex].getExternalBookRecord(); - int firstSheetIndex = getSheetIndex(ebrTarget.getSheetNames(), firstSheetName); - int lastSheetIndex = getSheetIndex(ebrTarget.getSheetNames(), lastSheetName); + String[] sheetNames = ebrTarget.getSheetNames(); + if (sheetNames == null) { + throw new IllegalStateException("Sheet names are not available for the ExternalBookRecord"); + } + int firstSheetIndex = getSheetIndex(sheetNames, firstSheetName); + int lastSheetIndex = getSheetIndex(sheetNames, lastSheetName); // Find or add the external sheet record definition for this int result = _externSheetRecord.getRefIxForSheet(externalBookIndex, firstSheetIndex, lastSheetIndex); diff --git a/poi/src/main/java/org/apache/poi/hssf/record/EscherAggregate.java b/poi/src/main/java/org/apache/poi/hssf/record/EscherAggregate.java index 6a0414c31c..fc6d8270b9 100644 --- a/poi/src/main/java/org/apache/poi/hssf/record/EscherAggregate.java +++ b/poi/src/main/java/org/apache/poi/hssf/record/EscherAggregate.java @@ -292,6 +292,9 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { // Write the matching OBJ record Record obj = shapeToObj.get(shapes.get(i)); + if (obj == null) { + throw new IllegalStateException("Cannot serialize EscherAggregate with missing shape-object"); + } pos += obj.serialize(pos, data); isFirst = false; diff --git a/poi/src/main/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/poi/src/main/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java index 5ce1f62f68..f8fd855050 100644 --- a/poi/src/main/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java +++ b/poi/src/main/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java @@ -39,7 +39,7 @@ import org.apache.poi.util.RecordFormatException; public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface { private final FormulaRecord _formulaRecord; - private SharedValueManager _sharedValueManager; + private final SharedValueManager _sharedValueManager; /** caches the calculated result of the formula */ private StringRecord _stringRecord; private SharedFormulaRecord _sharedFormulaRecord; @@ -195,6 +195,9 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel CellReference expRef = _formulaRecord.getFormula().getExpReference(); if (expRef != null) { ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol()); + if (arec == null) { + throw new IllegalStateException("Could not get ArrayRecord for cell-reference " + expRef.formatAsString()); + } return arec.getFormulaTokens(); } return _formulaRecord.getParsedExpression(); diff --git a/poi/src/main/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java b/poi/src/main/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java index 0a22a74f67..44763597cb 100644 --- a/poi/src/main/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java +++ b/poi/src/main/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java @@ -31,6 +31,7 @@ import org.apache.poi.hssf.record.TableRecord; import org.apache.poi.ss.formula.ptg.ExpPtg; import org.apache.poi.hssf.util.CellRangeAddress8Bit; import org.apache.poi.ss.util.CellReference; +import org.apache.poi.util.IOUtils; /** * Manages various auxiliary records while constructing a @@ -42,6 +43,7 @@ import org.apache.poi.ss.util.CellReference; * */ public final class SharedValueManager { + private static final int MAX_NUMBER_AGGS = 10_000; private static final class SharedFormulaGroup { private final SharedFormulaRecord _sfr; @@ -63,7 +65,12 @@ public final class SharedValueManager { _firstCell = firstCell; int width = sfr.getLastColumn() - sfr.getFirstColumn() + 1; int height = sfr.getLastRow() - sfr.getFirstRow() + 1; - _frAggs = new FormulaRecordAggregate[width * height]; + + // ensure we do not try to initialize a very large amount of formula-record-aggregates + int allocateSize = width * height; + IOUtils.safelyAllocateCheck(allocateSize, MAX_NUMBER_AGGS); + + _frAggs = new FormulaRecordAggregate[allocateSize]; _numberOfFormulas = 0; } diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFComment.java b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFComment.java index af6ac8b4f2..fabfcaa33c 100644 --- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFComment.java +++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFComment.java @@ -285,7 +285,11 @@ public class HSSFComment extends HSSFTextbox implements Comment { byte [] inSp = getEscherContainer().serialize(); spContainer.fillFields(inSp, 0, new DefaultEscherRecordFactory()); ObjRecord obj = (ObjRecord) getObjRecord().cloneViaReserialise(); - NoteRecord note = (NoteRecord) getNoteRecord().cloneViaReserialise(); + NoteRecord noteRecord = getNoteRecord(); + if (noteRecord == null) { + throw new IllegalStateException("Could not clone the note record for this comment because it is null"); + } + NoteRecord note = (NoteRecord) noteRecord.cloneViaReserialise(); return new HSSFComment(spContainer, obj, txo, note); } diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index ff2485cf6b..8421df2b65 100644 --- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -558,6 +558,9 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing */ @Override - public void resize(){ + public void resize() { resize(Double.MAX_VALUE); } @@ -152,7 +154,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { * @since 3.0.2 */ @Override - public HSSFClientAnchor getPreferredSize(){ + public HSSFClientAnchor getPreferredSize() { return getPreferredSize(1.0); } @@ -163,7 +165,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { * @return HSSFClientAnchor with the preferred size for this image * @since 3.0.2 */ - public HSSFClientAnchor getPreferredSize(double scale){ + public HSSFClientAnchor getPreferredSize(double scale) { return getPreferredSize(scale, scale); } @@ -176,7 +178,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { * @since 3.11 */ @Override - public HSSFClientAnchor getPreferredSize(double scaleX, double scaleY){ + public HSSFClientAnchor getPreferredSize(double scaleX, double scaleY) { ImageUtils.setPreferredSize(this, scaleX, scaleY); return getClientAnchor(); } @@ -187,7 +189,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { * @return image dimension in pixels */ @Override - public Dimension getImageDimension(){ + public Dimension getImageDimension() { InternalWorkbook iwb = getPatriarch().getSheet().getWorkbook().getWorkbook(); EscherBSERecord bse = iwb.getBSERecord(getPictureIndex()); byte[] data = bse.getBlipRecord().getPicturedata(); @@ -206,7 +208,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { * @return picture data for this shape or {@code null} if picture wasn't embedded, i.e. external linked */ @Override - public HSSFPictureData getPictureData(){ + public HSSFPictureData getPictureData() { int picIdx = getPictureIndex(); if (picIdx == -1) { return null; @@ -249,7 +251,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture { : StringUtil.getFromUnicodeLE(propFile.getComplexData()).trim(); } - public void setFileName(String data){ + public void setFileName(String data) { // TODO: add trailing \u0000? byte[] bytes = StringUtil.getToUnicodeLE(data); EscherComplexProperty prop = new EscherComplexProperty(EscherPropertyTypes.BLIP__BLIPFILENAME, true, bytes.length); diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShape.java index d9c98e3063..5946403a96 100644 --- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShape.java +++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShape.java @@ -115,6 +115,10 @@ public abstract class HSSFShape implements Shape { */ void setShapeId(int shapeId){ EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + if (spRecord == null) { + throw new IllegalStateException("Did not have an EscherSpRecord, cannot set shape id " + shapeId); + } + spRecord.setShapeId(shapeId); CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) _objRecord.getSubRecords().get(0); cod.setObjectId((short) (shapeId%1024)); diff --git a/poi/src/main/java/org/apache/poi/sl/draw/BitmapImageRenderer.java b/poi/src/main/java/org/apache/poi/sl/draw/BitmapImageRenderer.java index 0cf66780bd..cb3921eac6 100644 --- a/poi/src/main/java/org/apache/poi/sl/draw/BitmapImageRenderer.java +++ b/poi/src/main/java/org/apache/poi/sl/draw/BitmapImageRenderer.java @@ -289,7 +289,8 @@ public class BitmapImageRenderer implements ImageRenderer { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } - if (alpha == 0) { + if (alpha == 1) { + // Do not apply any rescale for a fully opaque alpha value return image; } diff --git a/poi/src/main/java/org/apache/poi/sl/draw/DrawPictureShape.java b/poi/src/main/java/org/apache/poi/sl/draw/DrawPictureShape.java index 0b9ae3cb13..2dbee57ea5 100644 --- a/poi/src/main/java/org/apache/poi/sl/draw/DrawPictureShape.java +++ b/poi/src/main/java/org/apache/poi/sl/draw/DrawPictureShape.java @@ -36,6 +36,9 @@ import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.PictureShape; import org.apache.poi.sl.usermodel.RectAlign; +import static org.apache.poi.sl.usermodel.PictureShape.FULLY_OPAQUE_ALPHA_VALUE; +import static org.apache.poi.sl.usermodel.PictureShape.FULLY_TRANSPARENT_ALPHA_VALUE; + public class DrawPictureShape extends DrawSimpleShape { private static final Logger LOG = PoiLogManager.getLogger(DrawPictureShape.class); @@ -50,6 +53,7 @@ public class DrawPictureShape extends DrawSimpleShape { Rectangle2D anchor = getAnchor(graphics, ps); Insets insets = ps.getClipping(); + int alpha = ps.getAlpha(); PictureData[] pics = { ps.getAlternativePictureData(), ps.getPictureData() }; for (PictureData data : pics) { @@ -66,6 +70,9 @@ public class DrawPictureShape extends DrawSimpleShape { ImageRenderer renderer = getImageRenderer(graphics, ct); if (renderer.canRender(ct)) { renderer.loadImage(dataBytes, ct); + if (FULLY_TRANSPARENT_ALPHA_VALUE <= alpha && alpha < FULLY_OPAQUE_ALPHA_VALUE) { + renderer.setAlpha(alpha/(float) FULLY_OPAQUE_ALPHA_VALUE); + } renderer.drawImage(graphics, anchor, insets); return; } diff --git a/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java index 189f9eabfe..28c43e6a95 100644 --- a/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/poi/src/main/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -591,10 +591,10 @@ public class DrawTextParagraph implements Drawable { final Map att = new HashMap<>(); final List attList = new ArrayList<>(); - for (TextRun run : paragraph){ + for (TextRun run : paragraph) { String runText = getRenderableText(graphics, run); // skip empty runs - if (runText.isEmpty()) { + if (runText == null || runText.isEmpty()) { continue; } diff --git a/poi/src/main/java/org/apache/poi/sl/draw/geom/ArcToCommandIf.java b/poi/src/main/java/org/apache/poi/sl/draw/geom/ArcToCommandIf.java index 6e5942ea89..17b6d99191 100644 --- a/poi/src/main/java/org/apache/poi/sl/draw/geom/ArcToCommandIf.java +++ b/poi/src/main/java/org/apache/poi/sl/draw/geom/ArcToCommandIf.java @@ -60,6 +60,10 @@ public interface ArcToCommandIf extends PathCommand { double invStart = Math.atan2(rx * Math.sin(radStart), ry * Math.cos(radStart)); Point2D pt = path.getCurrentPoint(); + if (pt == null) { + throw new IllegalStateException("Cannot draw arc without valid point"); + } + // calculate top/left corner double x0 = pt.getX() - rx * Math.cos(invStart) - rx; double y0 = pt.getY() - ry * Math.sin(invStart) - ry; diff --git a/poi/src/main/java/org/apache/poi/sl/usermodel/PictureShape.java b/poi/src/main/java/org/apache/poi/sl/usermodel/PictureShape.java index 9f967207bb..bc3dff1e4b 100644 --- a/poi/src/main/java/org/apache/poi/sl/usermodel/PictureShape.java +++ b/poi/src/main/java/org/apache/poi/sl/usermodel/PictureShape.java @@ -23,6 +23,9 @@ public interface PictureShape< S extends Shape, P extends TextParagraph > extends SimpleShape { + public static final int FULLY_TRANSPARENT_ALPHA_VALUE = 0; + public static final int FULLY_OPAQUE_ALPHA_VALUE = 100000; + /** * Returns the picture data for this picture. * @@ -47,4 +50,12 @@ public interface PictureShape< * @return the clipping rectangle, which is given in percent in relation to the image width/height */ Insets getClipping(); + + /** + * Returns alpha value in a range between 0 and 100000. + * Value 0 represents complete transparency and 100000 represents complete opacity. + * @return alpha value in the range 0..100000. + * @since 6.0.0 + */ + int getAlpha(); } diff --git a/poi/src/main/java/org/apache/poi/sl/usermodel/SlideShow.java b/poi/src/main/java/org/apache/poi/sl/usermodel/SlideShow.java index e29cb4a381..38ca6821ee 100644 --- a/poi/src/main/java/org/apache/poi/sl/usermodel/SlideShow.java +++ b/poi/src/main/java/org/apache/poi/sl/usermodel/SlideShow.java @@ -130,7 +130,7 @@ public interface SlideShow< /** * @return the instance which handles the persisting of the slideshow, * which is either a subclass of {@link org.apache.poi.POIDocument} - * or {@link org.apache.poi.ooxml.POIXMLDocument} + * or org.apache.poi.ooxml.POIXMLDocument * * @since 4.0.0 */ diff --git a/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java b/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java index a59d8368e4..aa895e7fee 100644 --- a/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java +++ b/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java @@ -260,7 +260,7 @@ public class EmbeddedExtractor implements Iterable { int pictureBytesLen = idxEnd-idxStart+6; byte[] pdfBytes = IOUtils.safelyClone(pictureBytes, idxStart, pictureBytesLen, MAX_RECORD_LENGTH); - String filename = source.getShapeName().trim(); + String filename = source.getShapeName() == null ? "empty" : source.getShapeName().trim(); if (!endsWithIgnoreCase(filename, ".pdf")) { filename += ".pdf"; } diff --git a/poi/src/main/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java b/poi/src/main/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java index 0b6e2b17bc..a78c636fa8 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java @@ -36,7 +36,7 @@ import org.apache.poi.ss.usermodel.Workbook; /** * An alternative workbook evaluator that saves memory in situations where a single workbook is * concurrently and independently evaluated many times. With standard formula evaluation, around - * 90% of memory consumption is due to loading of the {@link HSSFWorkbook} or {@link org.apache.poi.xssf.usermodel.XSSFWorkbook}. + * 90% of memory consumption is due to loading of the {@link HSSFWorkbook} or org.apache.poi.xssf.usermodel.XSSFWorkbook. * This class enables a 'master workbook' to be loaded just once and shared between many evaluation * clients. Each evaluation client creates its own {@link ForkedEvaluator} and can set cell values * that will be used for local evaluations (and don't disturb evaluations on other evaluators). diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/ClientAnchor.java b/poi/src/main/java/org/apache/poi/ss/usermodel/ClientAnchor.java index 4469f4ba09..e706453060 100644 --- a/poi/src/main/java/org/apache/poi/ss/usermodel/ClientAnchor.java +++ b/poi/src/main/java/org/apache/poi/ss/usermodel/ClientAnchor.java @@ -121,6 +121,16 @@ public interface ClientAnchor { */ public void setCol1(int col1); + /** + * Sets the column (0 based) of the first cell. + * + * @param col1 0-based column of the first cell. + * @since 6.0.0 + */ + default void setCol1(short col1) { + setCol1((int) col1); + } + /** * Returns the column (0 based) of the second cell, or -1 if there is no bottom-right anchor cell. * This is the case for absolute positioning ({@link AnchorType#DONT_MOVE_AND_RESIZE}) @@ -137,12 +147,23 @@ public interface ClientAnchor { */ public void setCol2(int col2); + /** + * Sets the column (0 based) of the second cell. + * + * @param col2 0-based column of the second cell. + * @since 6.0.0 + */ + default void setCol2(short col2) { + setCol2((int) col2); + } + /** * Returns the row (0 based) of the first cell, or -1 if there is no bottom-right anchor cell. * This is the case for absolute positioning ({@link AnchorType#DONT_MOVE_AND_RESIZE}). * * @return 0-based row of the first cell or -1 if none. */ + public int getRow1(); /** diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/ExtendedColor.java b/poi/src/main/java/org/apache/poi/ss/usermodel/ExtendedColor.java index ed932b13f3..5c4ca62e74 100644 --- a/poi/src/main/java/org/apache/poi/ss/usermodel/ExtendedColor.java +++ b/poi/src/main/java/org/apache/poi/ss/usermodel/ExtendedColor.java @@ -20,7 +20,7 @@ import java.util.Locale; /** * Represents a XSSF-style color (based on either a - * {@link org.apache.poi.xssf.usermodel.XSSFColor} or a + * org.apache.poi.xssf.usermodel.XSSFColor or a * {@link org.apache.poi.hssf.record.common.ExtendedColor} */ public abstract class ExtendedColor implements Color { diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java b/poi/src/main/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java index 2ef2921e07..7833100c20 100644 --- a/poi/src/main/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java +++ b/poi/src/main/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java @@ -30,16 +30,16 @@ import org.apache.poi.util.LocaleUtil; * Helper for shifting rows up or down */ // non-Javadoc: This abstract class exists to consolidate duplicated code between -// {@link org.apache.poi.hssf.usermodel.helpers.HSSFRowShifter} and -// {@link org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter} +// org.apache.poi.hssf.usermodel.helpers.HSSFRowShifter and +// org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter // (currently methods sprinkled throughout HSSFSheet) public abstract class RowShifter extends BaseRowColShifter { protected final Sheet sheet; - public RowShifter(Sheet sh) { - sheet = sh; - } - + public RowShifter(Sheet sh) { + sheet = sh; + } + /** * Shifts, grows, or shrinks the merged regions due to a row shift. * Merged regions that are completely overlaid by shifting will be deleted. @@ -83,7 +83,7 @@ public abstract class RowShifter extends BaseRowColShifter { removedIndices.add(i); } } - + if(!removedIndices.isEmpty()) { sheet.removeMergedRegions(removedIndices); } diff --git a/poi/src/main/java/org/apache/poi/util/IOUtils.java b/poi/src/main/java/org/apache/poi/util/IOUtils.java index 9dc7ee80c5..f7abf94319 100644 --- a/poi/src/main/java/org/apache/poi/util/IOUtils.java +++ b/poi/src/main/java/org/apache/poi/util/IOUtils.java @@ -460,7 +460,7 @@ public final class IOUtils { public static long copy(InputStream srcStream, File destFile) throws IOException { File destDirectory = destFile.getParentFile(); if (!(destDirectory.exists() || destDirectory.mkdirs())) { - throw new IllegalStateException("Can't create destination directory: "+destDirectory); + throw new IllegalStateException("Can't create destination directory: " + destDirectory); } try (OutputStream destStream = Files.newOutputStream(destFile.toPath())) { return IOUtils.copy(srcStream, destStream); @@ -581,7 +581,7 @@ public final class IOUtils { throw new RecordFormatException("Can't allocate an array of length < 0, but had " + length + " and " + maxLength); } if (length > (long)Integer.MAX_VALUE) { - throw new RecordFormatException("Can't allocate an array > "+Integer.MAX_VALUE); + throw new RecordFormatException("Can't allocate an array > " + Integer.MAX_VALUE); } checkLength(length, maxLength); } @@ -643,7 +643,7 @@ public final class IOUtils { throw new RecordFormatException(String.format(Locale.ROOT, "Tried to allocate an array of length %,d" + ", but the maximum length for this record type is %,d.%n" + "If the file is not corrupt and not large, please open an issue on bugzilla to request %n" + - "increasing the maximum allowable size for this record type.%n"+ + "increasing the maximum allowable size for this record type.%n" + "You can set a higher override value with IOUtils.setByteArrayMaxOverride()", length, maxLength)); } @@ -651,7 +651,7 @@ public final class IOUtils { throw new RecordFormatException(String.format(Locale.ROOT, "Tried to read data but the maximum length " + "for this record type is %,d.%n" + "If the file is not corrupt and not large, please open an issue on bugzilla to request %n" + - "increasing the maximum allowable size for this record type.%n"+ + "increasing the maximum allowable size for this record type.%n" + "You can set a higher override value with IOUtils.setByteArrayMaxOverride()", maxLength)); } } diff --git a/poi/src/main/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategy.java b/poi/src/main/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategy.java index a9e5d2c0fe..08593cd2ed 100644 --- a/poi/src/main/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategy.java +++ b/poi/src/main/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategy.java @@ -1,50 +1,50 @@ -/* ==================================================================== - 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.poi.util; - -import java.io.IOException; -import java.nio.file.Path; - -/** - * Username-aware subclass of {@link DefaultTempFileCreationStrategy} - * that avoids permission issues when deploying applications with multiple users on the same server. - * Other than adding the username to the temporary directory, all other behavior is the same as the superclass. - * - * @since 5.4.0 - */ -public class UserNameAwareTempFileCreationStrategy extends DefaultTempFileCreationStrategy { - - /** - * JVM property for the current username. - */ - private static final String JAVA_PROP_USER_NAME = "user.name"; - - @Override - protected Path getPOIFilesDirectoryPath() throws IOException { - final String tmpDir = getJavaIoTmpDir(); - String poifilesDir = POIFILES; - // Make the default temporary directory contain the username to avoid permission issues - // when deploying applications on the same server with multiple users - String username = System.getProperty(JAVA_PROP_USER_NAME); - if (null != username && !username.isEmpty()) { - poifilesDir += "_" + username; - } - return Path.of(tmpDir, poifilesDir); - } - -} +/* ==================================================================== + 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.poi.util; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * Username-aware subclass of {@link DefaultTempFileCreationStrategy} + * that avoids permission issues when deploying applications with multiple users on the same server. + * Other than adding the username to the temporary directory, all other behavior is the same as the superclass. + * + * @since 5.4.0 + */ +public class UserNameAwareTempFileCreationStrategy extends DefaultTempFileCreationStrategy { + + /** + * JVM property for the current username. + */ + private static final String JAVA_PROP_USER_NAME = "user.name"; + + @Override + protected Path getPOIFilesDirectoryPath() throws IOException { + final String tmpDir = getJavaIoTmpDir(); + String poifilesDir = POIFILES; + // Make the default temporary directory contain the username to avoid permission issues + // when deploying applications on the same server with multiple users + String username = System.getProperty(JAVA_PROP_USER_NAME); + if (null != username && !username.isEmpty()) { + poifilesDir += "_" + username; + } + return Path.of(tmpDir, poifilesDir); + } + +} diff --git a/poi/src/main/resources/org/apache/poi/sl/draw/geom/presetShapeDefinitions.xml b/poi/src/main/resources/org/apache/poi/sl/draw/geom/presetShapeDefinitions.xml index f3c3f2ca33..b425872e06 100644 --- a/poi/src/main/resources/org/apache/poi/sl/draw/geom/presetShapeDefinitions.xml +++ b/poi/src/main/resources/org/apache/poi/sl/draw/geom/presetShapeDefinitions.xml @@ -1,4 +1,4 @@ - + diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/BaseTestIteratingXLS.java b/poi/src/test/java/org/apache/poi/hssf/dev/BaseTestIteratingXLS.java index 5ac8864f17..bdf55860f4 100644 --- a/poi/src/test/java/org/apache/poi/hssf/dev/BaseTestIteratingXLS.java +++ b/poi/src/test/java/org/apache/poi/hssf/dev/BaseTestIteratingXLS.java @@ -28,6 +28,7 @@ import java.util.stream.Stream; import org.apache.poi.POIDataSamples; import org.apache.poi.hssf.OldExcelFormatException; +import org.apache.poi.poifs.filesystem.OfficeXmlFileException; import org.apache.poi.util.RecordFormatException; import org.apache.tools.ant.DirectoryScanner; import org.junit.jupiter.api.TestInstance; @@ -91,12 +92,14 @@ public abstract class BaseTestIteratingXLS { excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4819588401201152.xls", RuntimeException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6537773940867072.xls", RuntimeException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4651309315719168.xls", RuntimeException.class); + excludes.put("rde.imf.ru_sites_default_files_rde_documents_vodootvedenie_2020.xlsb.xls", OfficeXmlFileException.class); + return excludes; } @ParameterizedTest @MethodSource("files") - void testMain(File file, Class t) throws Exception { + void testMain(File file, Class t) { // avoid running files leftover from previous failed runs // or created by tests running in parallel // otherwise this would cause sporadic failures with diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java index b9e7d3050a..1c17bb1506 100644 --- a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java +++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java @@ -36,6 +36,7 @@ import org.apache.poi.hssf.usermodel.HSSFPatriarch; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.util.RecordFormatException; import org.apache.poi.util.StringUtil; import org.apache.tools.ant.util.NullOutputStream; @@ -57,6 +58,7 @@ class TestBiffDrawingToXml extends BaseTestIteratingXLS { excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5436547081830400.xls", IllegalArgumentException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5889658057523200.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4977868385681408.xls", IllegalArgumentException.class); + excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", RecordFormatException.class); return excludes; } diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java index 77f41647b7..9d71b5fccd 100644 --- a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java +++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java @@ -48,6 +48,8 @@ class TestBiffViewer extends BaseTestIteratingXLS { excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", IndexOutOfBoundsException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class); + excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class); + excludes.put("cf9f845e73447b092477d0472402a5baea4b8c9f.xls", RecordFormatException.class); return excludes; } diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java index 29b31dec81..994a09e8ea 100644 --- a/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java +++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java @@ -45,6 +45,7 @@ class TestFormulaViewer extends BaseTestIteratingXLS { excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class); // HSSFWorkbook cannot open it as well excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class); excludes.put("protected_66115.xls", EncryptedDocumentException.class); + excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class); return excludes; } diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java index d244f3ff5a..033c201dae 100644 --- a/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java +++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java @@ -51,6 +51,8 @@ class TestRecordLister extends BaseTestIteratingXLS { excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6137883240824832.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-6483562584932352.xls", RecordFormatException.class); excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5816431116615680.xls", RecordFormatException.class); + excludes.put("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4734163573080064.xls", IndexOutOfBoundsException.class); + excludes.put("cf9f845e73447b092477d0472402a5baea4b8c9f.xls", RecordFormatException.class); return excludes; } diff --git a/poi/src/test/java/org/apache/poi/hssf/model/TestDrawingAggregate.java b/poi/src/test/java/org/apache/poi/hssf/model/TestDrawingAggregate.java index 8d07fc4d0e..f326f17b07 100644 --- a/poi/src/test/java/org/apache/poi/hssf/model/TestDrawingAggregate.java +++ b/poi/src/test/java/org/apache/poi/hssf/model/TestDrawingAggregate.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.model; import static org.apache.poi.poifs.storage.RawDataUtil.decompress; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -136,7 +137,9 @@ class TestDrawingAggregate { return Stream.of(files). filter(file -> !file.getName().equals("clusterfuzz-testcase-minimized-POIHSSFFuzzer-5285517825277952.xls") && - !file.getName().equals("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4977868385681408.xls")). + !file.getName().equals("clusterfuzz-testcase-minimized-POIHSSFFuzzer-4977868385681408.xls") && + !file.getName().equals("crash-e329fca9087fe21bca4a80c8bc472a661c98d860.xls") && + !file.getName().equals("cf9f845e73447b092477d0472402a5baea4b8c9f.xls")). map(Arguments::of); } @@ -359,8 +362,9 @@ class TestDrawingAggregate { // the sheet's drawing is not aggregated assertEquals(394, records.size(), "wrong size of sheet records stream"); // the last record before the drawing block - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); // records to be aggregated List dgRecords = records.subList(19, 389); @@ -380,7 +384,7 @@ class TestDrawingAggregate { } // the first record after the drawing block - assertTrue(records.get(389) instanceof WindowTwoRecord, "records.get(389) is expected to be Window2"); + assertInstanceOf(WindowTwoRecord.class, records.get(389), "records.get(389) is expected to be Window2"); // aggregate drawing records. // The subrange [19, 388] is expected to be replaced with a EscherAggregate object @@ -389,12 +393,13 @@ class TestDrawingAggregate { EscherAggregate agg = (EscherAggregate) records.get(loc); assertEquals(25, records.size(), "wrong size of the aggregated sheet records stream"); - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); - assertTrue(records.get(19) instanceof EscherAggregate, - "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); - assertTrue(records.get(20) instanceof WindowTwoRecord, - "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); + assertInstanceOf(EscherAggregate.class, records.get(19), + "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); + assertInstanceOf(WindowTwoRecord.class, records.get(20), + "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); byte[] dgBytesAfterSave = agg.serialize(); assertEquals(dgBytes.length, dgBytesAfterSave.length, "different size of drawing data before and after save"); @@ -423,8 +428,9 @@ class TestDrawingAggregate { // the sheet's drawing is not aggregated assertEquals(32, records.size(), "wrong size of sheet records stream"); // the last record before the drawing block - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); // records to be aggregated List dgRecords = records.subList(19, 26); @@ -444,7 +450,7 @@ class TestDrawingAggregate { byte[] dgBytes = toByteArray(dgRecords); // the first record after the drawing block - assertTrue(records.get(26) instanceof WindowTwoRecord, "records.get(26) is expected to be Window2"); + assertInstanceOf(WindowTwoRecord.class, records.get(26), "records.get(26) is expected to be Window2"); // aggregate drawing records. // The subrange [19, 38] is expected to be replaced with a EscherAggregate object @@ -453,12 +459,13 @@ class TestDrawingAggregate { EscherAggregate agg = (EscherAggregate) records.get(loc); assertEquals(26, records.size(), "wrong size of the aggregated sheet records stream"); - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); - assertTrue(records.get(19) instanceof EscherAggregate, - "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); - assertTrue(records.get(20) instanceof WindowTwoRecord, - "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); + assertInstanceOf(EscherAggregate.class, records.get(19), + "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); + assertInstanceOf(WindowTwoRecord.class, records.get(20), + "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); byte[] dgBytesAfterSave = agg.serialize(); assertEquals(dgBytes.length, dgBytesAfterSave.length, "different size of drawing data before and after save"); @@ -504,8 +511,9 @@ class TestDrawingAggregate { // the sheet's drawing is not aggregated assertEquals(46, records.size(), "wrong size of sheet records stream"); // the last record before the drawing block - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); // records to be aggregated List dgRecords = records.subList(19, 39); @@ -525,7 +533,7 @@ class TestDrawingAggregate { byte[] dgBytes = toByteArray(dgRecords); // the first record after the drawing block - assertTrue(records.get(39) instanceof WindowTwoRecord, "records.get(39) is expected to be Window2"); + assertInstanceOf(WindowTwoRecord.class, records.get(39), "records.get(39) is expected to be Window2"); // aggregate drawing records. // The subrange [19, 38] is expected to be replaced with a EscherAggregate object @@ -534,12 +542,13 @@ class TestDrawingAggregate { EscherAggregate agg = (EscherAggregate) records.get(loc); assertEquals(27, records.size(), "wrong size of the aggregated sheet records stream"); - assertTrue(records.get(18) instanceof RowRecordsAggregate, - "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass().getSimpleName()); - assertTrue(records.get(19) instanceof EscherAggregate, - "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); - assertTrue(records.get(20) instanceof WindowTwoRecord, - "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(18), + "records.get(18) is expected to be RowRecordsAggregate but was " + records.get(18).getClass() + .getSimpleName()); + assertInstanceOf(EscherAggregate.class, records.get(19), + "records.get(19) is expected to be EscherAggregate but was " + records.get(19).getClass().getSimpleName()); + assertInstanceOf(WindowTwoRecord.class, records.get(20), + "records.get(20) is expected to be Window2 but was " + records.get(20).getClass().getSimpleName()); byte[] dgBytesAfterSave = agg.serialize(); assertEquals(dgBytes.length, dgBytesAfterSave.length, "different size of drawing data before and after save"); @@ -561,8 +570,9 @@ class TestDrawingAggregate { // the sheet's drawing is not aggregated assertEquals(315, records.size(), "wrong size of sheet records stream"); // the last record before the drawing block - assertTrue(records.get(21) instanceof RowRecordsAggregate, - "records.get(21) is expected to be RowRecordsAggregate but was " + records.get(21).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(21), + "records.get(21) is expected to be RowRecordsAggregate but was " + records.get(21).getClass() + .getSimpleName()); // records to be aggregated List dgRecords = records.subList(22, 300); @@ -581,7 +591,7 @@ class TestDrawingAggregate { byte[] dgBytes = toByteArray(dgRecords); // the first record after the drawing block - assertTrue(records.get(300) instanceof WindowTwoRecord, "records.get(300) is expected to be Window2"); + assertInstanceOf(WindowTwoRecord.class, records.get(300), "records.get(300) is expected to be Window2"); // aggregate drawing records. // The subrange [19, 299] is expected to be replaced with a EscherAggregate object @@ -590,12 +600,13 @@ class TestDrawingAggregate { EscherAggregate agg = (EscherAggregate) records.get(loc); assertEquals(38, records.size(), "wrong size of the aggregated sheet records stream"); - assertTrue(records.get(21) instanceof RowRecordsAggregate, - "records.get(21) is expected to be RowRecordsAggregate but was " + records.get(21).getClass().getSimpleName()); - assertTrue(records.get(22) instanceof EscherAggregate, - "records.get(22) is expected to be EscherAggregate but was " + records.get(22).getClass().getSimpleName()); - assertTrue(records.get(23) instanceof WindowTwoRecord, - "records.get(23) is expected to be Window2 but was " + records.get(23).getClass().getSimpleName()); + assertInstanceOf(RowRecordsAggregate.class, records.get(21), + "records.get(21) is expected to be RowRecordsAggregate but was " + records.get(21).getClass() + .getSimpleName()); + assertInstanceOf(EscherAggregate.class, records.get(22), + "records.get(22) is expected to be EscherAggregate but was " + records.get(22).getClass().getSimpleName()); + assertInstanceOf(WindowTwoRecord.class, records.get(23), + "records.get(23) is expected to be Window2 but was " + records.get(23).getClass().getSimpleName()); byte[] dgBytesAfterSave = agg.serialize(); assertEquals(dgBytes.length, dgBytesAfterSave.length, "different size of drawing data before and after save"); @@ -737,8 +748,8 @@ class TestDrawingAggregate { sheet.aggregateDrawingRecords(drawingManager, false); assertEquals(2, records.size(), "drawing was not fully aggregated"); - assertTrue(records.get(0) instanceof EscherAggregate, "expected EscherAggregate"); - assertTrue(records.get(1) instanceof EOFRecord, "expected EOFRecord"); + assertInstanceOf(EscherAggregate.class, records.get(0), "expected EscherAggregate"); + assertInstanceOf(EOFRecord.class, records.get(1), "expected EOFRecord"); EscherAggregate agg = (EscherAggregate) records.get(0); byte[] dgBytesAfterSave = agg.serialize(); @@ -902,8 +913,8 @@ class TestDrawingAggregate { sheet.aggregateDrawingRecords(drawingManager, false); assertEquals(2, records.size(), "drawing was not fully aggregated"); - assertTrue(records.get(0) instanceof EscherAggregate, "expected EscherAggregate"); - assertTrue(records.get(1) instanceof EOFRecord, "expected EOFRecord"); + assertInstanceOf(EscherAggregate.class, records.get(0), "expected EscherAggregate"); + assertInstanceOf(EOFRecord.class, records.get(1), "expected EOFRecord"); EscherAggregate agg = (EscherAggregate) records.get(0); diff --git a/poi/src/test/java/org/apache/poi/hssf/model/TestInternalSheet.java b/poi/src/test/java/org/apache/poi/hssf/model/TestInternalSheet.java new file mode 100644 index 0000000000..db82c1ec7e --- /dev/null +++ b/poi/src/test/java/org/apache/poi/hssf/model/TestInternalSheet.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. +==================================================================== */ +package org.apache.poi.hssf.model; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; + +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.util.RecordFormatException; +import org.junit.jupiter.api.Test; + +class TestInternalSheet { + @Test + void testEmptySheet() { + InternalSheet sheet = InternalSheet.createSheet(); + sheet.groupColumnRange(0, 0, true); + sheet.groupRowRange(0, 0, true); + sheet.setDefaultColumnStyle(0, 0); + } + + @Test + void testMissingBOFRecord() { + assertThrows(RecordFormatException.class, + () -> InternalSheet.createSheet(new RecordStream( + List.of(new BOFRecord()), 0))); + } + + + @Test + void testInvalidBOFRecord() { + assertThrows(RecordFormatException.class, + () -> InternalSheet.createSheet(new RecordStream( + List.of(new BOFRecord()), 0))); + } + + @Test + void testInvalidBOFRecord2() { + assertThrows(RecordFormatException.class, + () -> InternalSheet.createSheet(new RecordStream( + List.of(BOFRecord.createSheetBOF()), 0))); + } + + @Test + void testEmptyRecordStream() { + InternalSheet sheet = InternalSheet.createSheet(new RecordStream( + List.of(BOFRecord.createSheetBOF(), + new WindowTwoRecord(), + EOFRecord.instance), 0)); + assertThrows(IllegalStateException.class, + () -> sheet.groupColumnRange(0, 0, true)); + sheet.groupRowRange(0, 0, true); + assertThrows(IllegalStateException.class, + () -> sheet.setDefaultColumnStyle(0, 0)); + } +} diff --git a/poi/src/test/java/org/apache/poi/hssf/model/TestLinkTable.java b/poi/src/test/java/org/apache/poi/hssf/model/TestLinkTable.java index 57355c3de5..be862dd1aa 100644 --- a/poi/src/test/java/org/apache/poi/hssf/model/TestLinkTable.java +++ b/poi/src/test/java/org/apache/poi/hssf/model/TestLinkTable.java @@ -141,6 +141,9 @@ final class TestLinkTable { // Bug 47001b: Expected an EXTERNSHEET record but got (org.apache.poi.hssf.record.SSTRecord) LinkTable lt = new LinkTable(recList, 0, wrl, Collections.emptyMap()); assertNotNull(lt); + + assertThrows(IllegalStateException.class, + () -> lt.getExternalBookAndSheetName(0)); } @Test diff --git a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFComment.java b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFComment.java index 5644733467..0420827997 100644 --- a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFComment.java +++ b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFComment.java @@ -22,10 +22,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherSpRecord; import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFTestDataSamples; @@ -362,6 +364,7 @@ final class TestHSSFComment extends BaseTestCellComment { void existingFileWithComment() throws IOException { try (HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("drawings.xls")) { HSSFSheet sheet = wb.getSheet("comments"); + assertNotNull(sheet); HSSFPatriarch drawing = sheet.getDrawingPatriarch(); assertEquals(1, drawing.getChildren().size()); HSSFComment comment = (HSSFComment) drawing.getChildren().get(0); @@ -434,4 +437,11 @@ final class TestHSSFComment extends BaseTestCellComment { assertEquals(2024, comment.getNoteRecord().getShapeId()); } } + + @Test + void getEmptyShape() throws IOException { + HSSFComment shape = new HSSFComment(new EscherContainerRecord(), null, null, null); + assertThrows(IllegalStateException.class, + () -> shape.setShapeId(1)); + } } diff --git a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFPicture.java b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFPicture.java index 3ff14c846e..aa5d42ba2f 100644 --- a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFPicture.java +++ b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFPicture.java @@ -21,6 +21,7 @@ import static org.apache.poi.hssf.HSSFTestDataSamples.openSampleWorkbook; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.io.IOException; import java.util.Arrays; @@ -28,6 +29,10 @@ import java.util.List; import org.apache.poi.POIDataSamples; import org.apache.poi.ddf.EscherBSERecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherPropertyTypes; +import org.apache.poi.ddf.EscherTextboxRecord; import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.InternalSheet; @@ -285,4 +290,25 @@ final class TestHSSFPicture extends BaseTestPicture { } } + @Test + void testEmptyOptRecord() throws IOException { + try (HSSFWorkbook wb = new HSSFWorkbook()) { + HSSFSheet sh = wb.createSheet("Pictures"); + HSSFPatriarch dr = sh.createDrawingPatriarch(); + HSSFShapeGroup gr = dr.createGroup(new HSSFClientAnchor()); + HSSFPicture pic = new HSSFPicture(gr, new HSSFClientAnchor()) { + @Override + protected EscherContainerRecord createSpContainer() { + EscherContainerRecord spContainer = super.createSpContainer(); + EscherOptRecord opt = spContainer.getChildById(EscherOptRecord.RECORD_ID); + spContainer.removeChildRecord(opt); + spContainer.removeChildRecord(spContainer.getChildById(EscherTextboxRecord.RECORD_ID)); + return spContainer; + } + }; + + assertNull(pic.getOptRecord()); + assertEquals(-1, pic.getPictureIndex()); + } + } } diff --git a/poi/src/test/java/org/apache/poi/ss/usermodel/TestClientAnchorShortSetter.java b/poi/src/test/java/org/apache/poi/ss/usermodel/TestClientAnchorShortSetter.java new file mode 100644 index 0000000000..2bbee1df48 --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/usermodel/TestClientAnchorShortSetter.java @@ -0,0 +1,66 @@ +/* ==================================================================== + 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.poi.ss.usermodel; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for the short-based ClientAnchor column setters introduced for Bug 69935. + */ +public class TestClientAnchorShortSetter { + + @Test + void shortSettersDelegateToIntSetters() throws Exception { + try (Workbook wb = new HSSFWorkbook()) { + CreationHelper helper = wb.getCreationHelper(); + + // The actual implementation returned here is HSSFClientAnchor + ClientAnchor anchor = helper.createClientAnchor(); + + // Use the new short-based overloads + anchor.setCol1((short) 2); + anchor.setCol2((short) 5); + + // Setting rows is not strictly required, but keeps the anchor fully initialized + anchor.setRow1(1); + anchor.setRow2(3); + + // getColX() still returns short; compare using int for convenience + assertEquals(2, anchor.getCol1()); + assertEquals(5, anchor.getCol2()); + } + } + + @Test + void intSettersStillWorkAsBefore() throws Exception { + try (Workbook wb = new HSSFWorkbook()) { + CreationHelper helper = wb.getCreationHelper(); + + ClientAnchor anchor = helper.createClientAnchor(); + + // Verify that the existing int-based setters still behave as before + anchor.setCol1(3); + anchor.setCol2(7); + + assertEquals(3, anchor.getCol1()); + assertEquals(7, anchor.getCol2()); + } + } +} diff --git a/poi/src/test/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategyTest.java b/poi/src/test/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategyTest.java index fd4b9267a6..0423d36299 100644 --- a/poi/src/test/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategyTest.java +++ b/poi/src/test/java/org/apache/poi/util/UserNameAwareTempFileCreationStrategyTest.java @@ -1,42 +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. -==================================================================== */ - -package org.apache.poi.util; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Path; - -import static org.apache.poi.util.DefaultTempFileCreationStrategy.POIFILES; -import static org.junit.jupiter.api.Assertions.assertEquals; - -class UserNameAwareTempFileCreationStrategyTest { - - @Test - void getPOIFilesDirectoryPath() throws IOException { - UserNameAwareTempFileCreationStrategy strategy = new UserNameAwareTempFileCreationStrategy(); - String tmpDir = System.getProperty(TempFile.JAVA_IO_TMPDIR); - String username = System.getProperty("user.name"); - String expectedPath = Path.of(tmpDir, POIFILES + "_" + username).toString(); - - Path actualPath = strategy.getPOIFilesDirectoryPath(); - - assertEquals(expectedPath, actualPath.toString()); - } - -} +/* ==================================================================== + 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.poi.util; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.apache.poi.util.DefaultTempFileCreationStrategy.POIFILES; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UserNameAwareTempFileCreationStrategyTest { + + @Test + void getPOIFilesDirectoryPath() throws IOException { + UserNameAwareTempFileCreationStrategy strategy = new UserNameAwareTempFileCreationStrategy(); + String tmpDir = System.getProperty(TempFile.JAVA_IO_TMPDIR); + String username = System.getProperty("user.name"); + String expectedPath = Path.of(tmpDir, POIFILES + "_" + username).toString(); + + Path actualPath = strategy.getPOIFilesDirectoryPath(); + + assertEquals(expectedPath, actualPath.toString()); + } + +} diff --git a/src/documentation/content/xdocs/components/index.xml b/src/documentation/content/xdocs/components/index.xml index 2a9103de85..ea8868daae 100644 --- a/src/documentation/content/xdocs/components/index.xml +++ b/src/documentation/content/xdocs/components/index.xml @@ -291,7 +291,7 @@ poi - log4j 2.x, + log4j 2.x, commons-codec, commons-collections, commons-math3 diff --git a/src/documentation/content/xdocs/devel/references/3rdparty.xml b/src/documentation/content/xdocs/devel/references/3rdparty.xml index 7a85232327..18286f9ab4 100644 --- a/src/documentation/content/xdocs/devel/references/3rdparty.xml +++ b/src/documentation/content/xdocs/devel/references/3rdparty.xml @@ -1,86 +1,86 @@ - - - - - -
- Third Party Contributions - - - -
- - - -
How to Contribute -

- See How to contribute to Poi. -

- -
- -
Contributed Components -

- These are not necessarily deemed to be high enough quality to be included in the - core distribution, but they have been tested under - several key environments, they are provided under the same license - as Poi, and they are included in the POI distribution under the - contrib/ directory. -

- -

- None as yet! - although you can expect that some of the links - listed below will eventually migrate to the "contributed components" level, and - then maybe even into the main distribution. -

-
- -
Patch Queue -

Submissions of modifications - to POI which are awaiting review. Anyone can - comment on them on the dev mailing list - code reviewers are needed! - Use these at your own risk - although POI has no guarantee - either, these patches have not been reviewed, let alone accepted. -

-
- -
Other Extensions -

The other extensions listed here are not endorsed by the POI - project either - they are provided as a convenience only. They may or may not work, - they may or may not be open source, etc. -

- -

To have a link added to this table, see How to contribute - to POI.

- - - - - - - - - - -
Name and LinkTypeDescriptionStatusLicensingContact
- -
- -
+ + + + + +
+ Third Party Contributions + + + +
+ + + +
How to Contribute +

+ See How to contribute to Poi. +

+ +
+ +
Contributed Components +

+ These are not necessarily deemed to be high enough quality to be included in the + core distribution, but they have been tested under + several key environments, they are provided under the same license + as Poi, and they are included in the POI distribution under the + contrib/ directory. +

+ +

+ None as yet! - although you can expect that some of the links + listed below will eventually migrate to the "contributed components" level, and + then maybe even into the main distribution. +

+
+ +
Patch Queue +

Submissions of modifications + to POI which are awaiting review. Anyone can + comment on them on the dev mailing list - code reviewers are needed! + Use these at your own risk - although POI has no guarantee + either, these patches have not been reviewed, let alone accepted. +

+
+ +
Other Extensions +

The other extensions listed here are not endorsed by the POI + project either - they are provided as a convenience only. They may or may not work, + they may or may not be open source, etc. +

+ +

To have a link added to this table, see How to contribute + to POI.

+ + + + + + + + + + +
Name and LinkTypeDescriptionStatusLicensingContact
+ +
+ +
diff --git a/src/documentation/content/xdocs/security.xml b/src/documentation/content/xdocs/security.xml index 358390181e..f82065f977 100644 --- a/src/documentation/content/xdocs/security.xml +++ b/src/documentation/content/xdocs/security.xml @@ -86,28 +86,36 @@ and writing xlsx files - so if you are working with large xlsx files, you should consider using the streaming APIs. +
  • Use of Temp Files
    + Apache POI makes significant use of temporary files. You need to ensure that the directory used + for temp files cannot be manipulated or even read by untrusted users. +
    + DefaultTempFileCreationStrategy is the default implementation but you can provide your own + strategy implementation. It is possible to configure POI to avoid temp file usage in some parts of + the code. +
  • Consider sandboxing document-parsing
    If you operate in a highly sensitive environment and would like to avoid any side effect from parsing documents on your application, then consider extracting the parsing logic into a separate process which is configured with appropriate memory settings and which you stop after some timeout. It is a good idea to be able to auto-restart the process in case of a crash. -
    +
  • Keep up to date with releases
    Apache POI does occasionally issue CVEs for security issues. There are also other bug fixes and improvements in each release. Some of these fixes will be to make POI more robust against malicious inputs, even if they are not explicitly security-related. -
    +
  • Monitor security advisories
    Keep an eye on security advisories related to Apache POI. You can find them on the POI website and they are shared on the POI mailing lists as well as the Apache Announce Mailing List. -
    +
    OpenCVE is one of a number of services that can help you monitor CVEs for specific products. -
    +
  • diff --git a/src/resources/devtools/forbidden-signatures.txt b/src/resources/devtools/forbidden-signatures.txt index ce9c56749f..8b9fca5f90 100644 --- a/src/resources/devtools/forbidden-signatures.txt +++ b/src/resources/devtools/forbidden-signatures.txt @@ -1,174 +1,174 @@ -# (C) Copyright Uwe Schindler (Generics Policeman) and others. -# Parts of this work are licensed to the Apache Software Foundation (ASF) -# under one or more contributor license agreements. -# -# Licensed 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. -# -# This file contains API signatures which are specific to POI. -# The goal is to minimize implicit defaults - -@ignoreMissingClasses -@defaultMessage POI forbidden APIs - -# Locale related interfaces which we want to avoid to not have code which depends on the locale of the current machine -java.util.Locale#getDefault() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details -java.util.Locale#setDefault(java.util.Locale) @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details -java.util.TimeZone#getDefault() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details -java.util.Date#toString() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details - -java.text.DecimalFormatSymbols#() @ use DecimalFormatSymbols.getInstance() -java.text.DecimalFormatSymbols#(java.util.Locale) @ use DecimalFormatSymbols.getInstance() - -# the following are taken from the Elasticsearch source at https://github.com/elastic/elasticsearch/tree/master/buildSrc/src/main/resources/forbidden - -@defaultMessage Convert to URI -java.net.URL#getPath() -java.net.URL#getFile() - -@defaultMessage Usage of getLocalHost is discouraged -java.net.InetAddress#getLocalHost() - -@defaultMessage Specify a location for the temp file/directory instead. -java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) -java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) - -@defaultMessage Specify a location for the temp file/directory instead. -java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) -java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) - -@defaultMessage Don't use java serialization - this can break BWC without noticing it -java.io.ObjectOutputStream -java.io.ObjectOutput -java.io.ObjectInputStream -java.io.ObjectInput - -@defaultMessage Resolve hosts explicitly to the address(es) you want with InetAddress. -java.net.InetSocketAddress#(java.lang.String,int) -java.net.Socket#(java.lang.String,int) -java.net.Socket#(java.lang.String,int,java.net.InetAddress,int) - -@defaultMessage Don't bind to wildcard addresses. Be specific. -java.net.DatagramSocket#() -java.net.DatagramSocket#(int) -java.net.InetSocketAddress#(int) -java.net.MulticastSocket#() -java.net.MulticastSocket#(int) -java.net.ServerSocket#(int) -java.net.ServerSocket#(int,int) - -@defaultMessage use NetworkAddress format/formatAddress to print IP or IP+ports -java.net.InetAddress#toString() -java.net.InetAddress#getHostAddress() -java.net.Inet4Address#getHostAddress() -java.net.Inet6Address#getHostAddress() -java.net.InetSocketAddress#toString() - -@defaultMessage avoid DNS lookups by accident: if you have a valid reason, then @SuppressWarnings with that reason so its completely clear -java.net.InetAddress#getHostName() -java.net.InetAddress#getCanonicalHostName() - -java.net.InetSocketAddress#getHostName() @ Use getHostString() instead, which avoids a DNS lookup - - -java.lang.Thread#getAllStackTraces() @ this method needs special permission -java.lang.Thread#getContextClassLoader() @ use getClass().getClassLoader() instead of getContextClassLoader() (see https://stackoverflow.com/a/36228195/2066598) - -@defaultMessage Avoid unchecked warnings by using Collections#empty(List|Map|Set) methods -java.util.Collections#EMPTY_LIST -java.util.Collections#EMPTY_MAP -java.util.Collections#EMPTY_SET - - -@defaultMessage spawns threads with vague names; use a custom thread factory and name threads so that you can tell (by its name) which executor it is associated with -java.util.concurrent.Executors#newFixedThreadPool(int) -java.util.concurrent.Executors#newSingleThreadExecutor() -java.util.concurrent.Executors#newCachedThreadPool() -java.util.concurrent.Executors#newSingleThreadScheduledExecutor() -java.util.concurrent.Executors#newScheduledThreadPool(int) -java.util.concurrent.Executors#defaultThreadFactory() -java.util.concurrent.Executors#privilegedThreadFactory() - -java.lang.Character#codePointBefore(char[],int) @ Implicit start offset is error-prone when the char[] is a buffer and the first chars are random chars -java.lang.Character#codePointAt(char[],int) @ Implicit end offset is error-prone when the char[] is a buffer and the last chars are random chars - -@defaultMessage specify a locale when using toUpperCase / to LowerCase - -java.lang.Character#isLowerCase(char) -java.lang.Character#isUpperCase(char) -java.lang.Character#toLowerCase(char) -java.lang.Character#toUpperCase(char) -java.lang.String#toLowerCase() -java.lang.String#toUpperCase() - -@defaultMessage Only use wait / notify when really needed try to use concurrency primitives, latches or callbacks instead. -java.lang.Object#wait() -java.lang.Object#wait(long) -java.lang.Object#wait(long,int) -java.lang.Object#notify() -java.lang.Object#notifyAll() - -@defaultMessage Don't interrupt threads use FutureUtils#cancel(Future) instead -java.util.concurrent.Future#cancel(boolean) - -@defaultMessage Don't use ...InputStream.available() as it gives wrong result for certain streams - use IOUtils.toByteArray to read the stream fully and then count the available bytes -java.io.InputStream#available() - -@defaultMessage Use newInstance, as newFactory does not seem to work on Android - https://github.com/centic9/poi-on-android/issues/44#issuecomment-426517981 -javax.xml.stream.XMLEventFactory#newFactory() -javax.xml.stream.XMLInputFactory#newFactory() -javax.xml.stream.XMLOutputFactory#newFactory() - -@defaultMessage Unnecessary, inefficient, and confusing conversion of String.toString -java.lang.String#toString() - -#@defaultMessage Deprecated Java APIs -#java.lang.StringBuffer -#java.util.Hashtable - -@defaultMessage DatatypeConverter is not available in Java 9+ without adding add-opens - use java.util.Base64 -javax.xml.bind.DatatypeConverter - -@defaultMessage don't rely on the threads ContextClassLoader - provide the classloader via load(Class, Classloader) -java.util.ServiceLoader#load(java.lang.Class) - -@defaultMessage Use Log4J classes instead -java.util.logging.** - -# taken from https://github.com/apache/solr/blob/main/gradle/validation/forbidden-apis/com.google.guava.guava.all.txt -@defaultMessage Use corresponding Java 8 functional/streaming interfaces -com.google.common.base.Function -com.google.common.base.Joiner -com.google.common.base.Predicate -com.google.common.base.Supplier - -@defaultMessage Use java.nio.charset.StandardCharsets instead -com.google.common.base.Charsets - -@defaultMessage Use methods in java.util.Objects instead -com.google.common.base.Objects#equal(java.lang.Object,java.lang.Object) -com.google.common.base.Objects#hashCode(java.lang.Object[]) -com.google.common.base.Preconditions#checkNotNull(java.lang.Object) -com.google.common.base.Preconditions#checkNotNull(java.lang.Object,java.lang.Object) - -@defaultMessage Use methods in java.util.Comparator instead -com.google.common.collect.Ordering - - -# taken from https://github.com/apache/solr/blob/main/gradle/validation/forbidden-apis/commons-codec.commons-codec.all.txt -@defaultMessage Use java.nio.charset.StandardCharsets instead -org.apache.commons.codec.Charsets - -@defaultMessage Use java.util.Base64 instead -org.apache.commons.codec.binary.Base64 - - +# (C) Copyright Uwe Schindler (Generics Policeman) and others. +# Parts of this work are licensed to the Apache Software Foundation (ASF) +# under one or more contributor license agreements. +# +# Licensed 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. +# +# This file contains API signatures which are specific to POI. +# The goal is to minimize implicit defaults + +@ignoreMissingClasses +@defaultMessage POI forbidden APIs + +# Locale related interfaces which we want to avoid to not have code which depends on the locale of the current machine +java.util.Locale#getDefault() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details +java.util.Locale#setDefault(java.util.Locale) @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details +java.util.TimeZone#getDefault() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details +java.util.Date#toString() @ Do not use methods that depend on the current Locale, either use Locale.ROOT or let the user define the locale, see class LocaleUtil for details + +java.text.DecimalFormatSymbols#() @ use DecimalFormatSymbols.getInstance() +java.text.DecimalFormatSymbols#(java.util.Locale) @ use DecimalFormatSymbols.getInstance() + +# the following are taken from the Elasticsearch source at https://github.com/elastic/elasticsearch/tree/master/buildSrc/src/main/resources/forbidden + +@defaultMessage Convert to URI +java.net.URL#getPath() +java.net.URL#getFile() + +@defaultMessage Usage of getLocalHost is discouraged +java.net.InetAddress#getLocalHost() + +@defaultMessage Specify a location for the temp file/directory instead. +java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) +java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) + +@defaultMessage Specify a location for the temp file/directory instead. +java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) +java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) + +@defaultMessage Don't use java serialization - this can break BWC without noticing it +java.io.ObjectOutputStream +java.io.ObjectOutput +java.io.ObjectInputStream +java.io.ObjectInput + +@defaultMessage Resolve hosts explicitly to the address(es) you want with InetAddress. +java.net.InetSocketAddress#(java.lang.String,int) +java.net.Socket#(java.lang.String,int) +java.net.Socket#(java.lang.String,int,java.net.InetAddress,int) + +@defaultMessage Don't bind to wildcard addresses. Be specific. +java.net.DatagramSocket#() +java.net.DatagramSocket#(int) +java.net.InetSocketAddress#(int) +java.net.MulticastSocket#() +java.net.MulticastSocket#(int) +java.net.ServerSocket#(int) +java.net.ServerSocket#(int,int) + +@defaultMessage use NetworkAddress format/formatAddress to print IP or IP+ports +java.net.InetAddress#toString() +java.net.InetAddress#getHostAddress() +java.net.Inet4Address#getHostAddress() +java.net.Inet6Address#getHostAddress() +java.net.InetSocketAddress#toString() + +@defaultMessage avoid DNS lookups by accident: if you have a valid reason, then @SuppressWarnings with that reason so its completely clear +java.net.InetAddress#getHostName() +java.net.InetAddress#getCanonicalHostName() + +java.net.InetSocketAddress#getHostName() @ Use getHostString() instead, which avoids a DNS lookup + + +java.lang.Thread#getAllStackTraces() @ this method needs special permission +java.lang.Thread#getContextClassLoader() @ use getClass().getClassLoader() instead of getContextClassLoader() (see https://stackoverflow.com/a/36228195/2066598) + +@defaultMessage Avoid unchecked warnings by using Collections#empty(List|Map|Set) methods +java.util.Collections#EMPTY_LIST +java.util.Collections#EMPTY_MAP +java.util.Collections#EMPTY_SET + + +@defaultMessage spawns threads with vague names; use a custom thread factory and name threads so that you can tell (by its name) which executor it is associated with +java.util.concurrent.Executors#newFixedThreadPool(int) +java.util.concurrent.Executors#newSingleThreadExecutor() +java.util.concurrent.Executors#newCachedThreadPool() +java.util.concurrent.Executors#newSingleThreadScheduledExecutor() +java.util.concurrent.Executors#newScheduledThreadPool(int) +java.util.concurrent.Executors#defaultThreadFactory() +java.util.concurrent.Executors#privilegedThreadFactory() + +java.lang.Character#codePointBefore(char[],int) @ Implicit start offset is error-prone when the char[] is a buffer and the first chars are random chars +java.lang.Character#codePointAt(char[],int) @ Implicit end offset is error-prone when the char[] is a buffer and the last chars are random chars + +@defaultMessage specify a locale when using toUpperCase / to LowerCase + +java.lang.Character#isLowerCase(char) +java.lang.Character#isUpperCase(char) +java.lang.Character#toLowerCase(char) +java.lang.Character#toUpperCase(char) +java.lang.String#toLowerCase() +java.lang.String#toUpperCase() + +@defaultMessage Only use wait / notify when really needed try to use concurrency primitives, latches or callbacks instead. +java.lang.Object#wait() +java.lang.Object#wait(long) +java.lang.Object#wait(long,int) +java.lang.Object#notify() +java.lang.Object#notifyAll() + +@defaultMessage Don't interrupt threads use FutureUtils#cancel(Future) instead +java.util.concurrent.Future#cancel(boolean) + +@defaultMessage Don't use ...InputStream.available() as it gives wrong result for certain streams - use IOUtils.toByteArray to read the stream fully and then count the available bytes +java.io.InputStream#available() + +@defaultMessage Use newInstance, as newFactory does not seem to work on Android - https://github.com/centic9/poi-on-android/issues/44#issuecomment-426517981 +javax.xml.stream.XMLEventFactory#newFactory() +javax.xml.stream.XMLInputFactory#newFactory() +javax.xml.stream.XMLOutputFactory#newFactory() + +@defaultMessage Unnecessary, inefficient, and confusing conversion of String.toString +java.lang.String#toString() + +#@defaultMessage Deprecated Java APIs +#java.lang.StringBuffer +#java.util.Hashtable + +@defaultMessage DatatypeConverter is not available in Java 9+ without adding add-opens - use java.util.Base64 +javax.xml.bind.DatatypeConverter + +@defaultMessage don't rely on the threads ContextClassLoader - provide the classloader via load(Class, Classloader) +java.util.ServiceLoader#load(java.lang.Class) + +@defaultMessage Use Log4J classes instead +java.util.logging.** + +# taken from https://github.com/apache/solr/blob/main/gradle/validation/forbidden-apis/com.google.guava.guava.all.txt +@defaultMessage Use corresponding Java 8 functional/streaming interfaces +com.google.common.base.Function +com.google.common.base.Joiner +com.google.common.base.Predicate +com.google.common.base.Supplier + +@defaultMessage Use java.nio.charset.StandardCharsets instead +com.google.common.base.Charsets + +@defaultMessage Use methods in java.util.Objects instead +com.google.common.base.Objects#equal(java.lang.Object,java.lang.Object) +com.google.common.base.Objects#hashCode(java.lang.Object[]) +com.google.common.base.Preconditions#checkNotNull(java.lang.Object) +com.google.common.base.Preconditions#checkNotNull(java.lang.Object,java.lang.Object) + +@defaultMessage Use methods in java.util.Comparator instead +com.google.common.collect.Ordering + + +# taken from https://github.com/apache/solr/blob/main/gradle/validation/forbidden-apis/commons-codec.commons-codec.all.txt +@defaultMessage Use java.nio.charset.StandardCharsets instead +org.apache.commons.codec.Charsets + +@defaultMessage Use java.util.Base64 instead +org.apache.commons.codec.binary.Base64 + + diff --git a/src/resources/ooxml-lite-report.clazz b/src/resources/ooxml-lite-report.clazz index 144216dba8..2069d3eef0 100644 --- a/src/resources/ooxml-lite-report.clazz +++ b/src/resources/ooxml-lite-report.clazz @@ -2551,3 +2551,6 @@ org/openxmlformats/schemas/drawingml/x2006/main/impl/CTAudioFileImpl org/openxmlformats/schemas/drawingml/x2006/main/CTAudioFile org/openxmlformats/schemas/drawingml/x2006/chart/impl/STHoleSizePercentImpl org/openxmlformats/schemas/drawingml/x2006/chart/impl/STHoleSizeUByteImpl +org/openxmlformats/schemas/drawingml/x2006/main/CTSupplementalFont +org/openxmlformats/schemas/drawingml/x2006/main/impl/CTSupplementalFontImpl +org/openxmlformats/schemas/officeDocument/x2006/sharedTypes/impl/STPositivePercentageImpl diff --git a/src/resources/ooxml-lite-report.xsb b/src/resources/ooxml-lite-report.xsb index e058876cc7..2120d975b8 100644 --- a/src/resources/ooxml-lite-report.xsb +++ b/src/resources/ooxml-lite-report.xsb @@ -1185,3 +1185,5 @@ ctpivotareareferencee5a5type ctindex5371type stholesizepercenta3d2type stholesizeubyte577atype +chartspace67aadoctype +ctsupplementalfonta06etype diff --git a/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-6312416206454784.vsdx b/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-6312416206454784.vsdx new file mode 100644 index 0000000000..e05ab739fa Binary files /dev/null and b/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-6312416206454784.vsdx differ diff --git a/test-data/document/chartex.docx b/test-data/document/chartex.docx new file mode 100644 index 0000000000..79b1cb639a Binary files /dev/null and b/test-data/document/chartex.docx differ diff --git a/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc new file mode 100644 index 0000000000..4f2d6de96c Binary files /dev/null and b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5832867957309440.doc differ diff --git a/test-data/document/clusterfuzz-testcase-minimized-POIXWPFFuzzer-4961551840247808.docx b/test-data/document/clusterfuzz-testcase-minimized-POIXWPFFuzzer-4961551840247808.docx new file mode 100644 index 0000000000..cb604f0cc5 Binary files /dev/null and b/test-data/document/clusterfuzz-testcase-minimized-POIXWPFFuzzer-4961551840247808.docx differ diff --git a/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-4918886059278336.pub b/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-4918886059278336.pub new file mode 100644 index 0000000000..c901951b19 Binary files /dev/null and b/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-4918886059278336.pub differ diff --git a/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-6325615354773504.pub b/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-6325615354773504.pub new file mode 100644 index 0000000000..99c698207f Binary files /dev/null and b/test-data/publisher/clusterfuzz-testcase-minimized-POIHPBFFuzzer-6325615354773504.pub differ diff --git a/test-data/slideshow/119877_all type background_save_by_AOO.ppt b/test-data/slideshow/119877_all type background_save_by_AOO.ppt new file mode 100644 index 0000000000..28cfff121d Binary files /dev/null and b/test-data/slideshow/119877_all type background_save_by_AOO.ppt differ diff --git a/test-data/slideshow/2100a8d44da546f97ab7795c500a58bed6cb655d.ppt b/test-data/slideshow/2100a8d44da546f97ab7795c500a58bed6cb655d.ppt new file mode 100644 index 0000000000..56815d6acc Binary files /dev/null and b/test-data/slideshow/2100a8d44da546f97ab7795c500a58bed6cb655d.ppt differ diff --git a/test-data/slideshow/2cade576206d5bf9a89479446973deeda5a9b549.ppt b/test-data/slideshow/2cade576206d5bf9a89479446973deeda5a9b549.ppt new file mode 100644 index 0000000000..265c8e505e Binary files /dev/null and b/test-data/slideshow/2cade576206d5bf9a89479446973deeda5a9b549.ppt differ diff --git a/test-data/slideshow/60f557c0a46bcb0068b1c3e15589dac383307bc8.ppt b/test-data/slideshow/60f557c0a46bcb0068b1c3e15589dac383307bc8.ppt new file mode 100644 index 0000000000..3d8459400c Binary files /dev/null and b/test-data/slideshow/60f557c0a46bcb0068b1c3e15589dac383307bc8.ppt differ diff --git a/test-data/slideshow/62d2ecd89aeb715b07072d4f8a8734f4dbeb5c10.ppt b/test-data/slideshow/62d2ecd89aeb715b07072d4f8a8734f4dbeb5c10.ppt new file mode 100644 index 0000000000..05256ad4a5 Binary files /dev/null and b/test-data/slideshow/62d2ecd89aeb715b07072d4f8a8734f4dbeb5c10.ppt differ diff --git a/test-data/slideshow/6afff8111a22b118d1ed4eba5007c153de0b0ad7.ppt b/test-data/slideshow/6afff8111a22b118d1ed4eba5007c153de0b0ad7.ppt new file mode 100644 index 0000000000..6bdb60f21a Binary files /dev/null and b/test-data/slideshow/6afff8111a22b118d1ed4eba5007c153de0b0ad7.ppt differ diff --git a/test-data/slideshow/7ffe3cabb976ebc593dfe2f9461bdeac980a626c.ppt b/test-data/slideshow/7ffe3cabb976ebc593dfe2f9461bdeac980a626c.ppt new file mode 100644 index 0000000000..439a103648 Binary files /dev/null and b/test-data/slideshow/7ffe3cabb976ebc593dfe2f9461bdeac980a626c.ppt differ diff --git a/test-data/slideshow/cf5f6fde99a8b3ea5a4946c258b7abad6f30b0c5.ppt b/test-data/slideshow/cf5f6fde99a8b3ea5a4946c258b7abad6f30b0c5.ppt new file mode 100644 index 0000000000..a92aac2d04 Binary files /dev/null and b/test-data/slideshow/cf5f6fde99a8b3ea5a4946c258b7abad6f30b0c5.ppt differ diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-6701721724125184.wmf b/test-data/slideshow/clusterfuzz-testcase-minimized-6701721724125184.wmf new file mode 100644 index 0000000000..abda269ddb Binary files /dev/null and b/test-data/slideshow/clusterfuzz-testcase-minimized-6701721724125184.wmf differ diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt b/test-data/slideshow/clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt new file mode 100644 index 0000000000..96793914be Binary files /dev/null and b/test-data/slideshow/clusterfuzz-testcase-minimized-POIHSLFFuzzer-6028723156746240.ppt differ diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6071540680032256.pptx b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6071540680032256.pptx new file mode 100644 index 0000000000..1d8d49a2b9 Binary files /dev/null and b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6071540680032256.pptx differ diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6372932378820608.pptx b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6372932378820608.pptx new file mode 100644 index 0000000000..c064fd422c Binary files /dev/null and b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-6372932378820608.pptx differ diff --git a/test-data/slideshow/crash-57308ca363f5b71763c489d1b432aff009d4bc4f.pptx b/test-data/slideshow/crash-57308ca363f5b71763c489d1b432aff009d4bc4f.pptx new file mode 100644 index 0000000000..7c6cd0987c Binary files /dev/null and b/test-data/slideshow/crash-57308ca363f5b71763c489d1b432aff009d4bc4f.pptx differ diff --git a/test-data/slideshow/crash-7b60e9fe792eaaf1bba8be90c2b62f057cfff142.emf b/test-data/slideshow/crash-7b60e9fe792eaaf1bba8be90c2b62f057cfff142.emf new file mode 100644 index 0000000000..32373a0409 Binary files /dev/null and b/test-data/slideshow/crash-7b60e9fe792eaaf1bba8be90c2b62f057cfff142.emf differ diff --git a/test-data/slideshow/file-45.wmf b/test-data/slideshow/file-45.wmf new file mode 100644 index 0000000000..6293588017 Binary files /dev/null and b/test-data/slideshow/file-45.wmf differ diff --git a/test-data/slideshow/picture-transparency.pptx b/test-data/slideshow/picture-transparency.pptx new file mode 100644 index 0000000000..41121f7dde Binary files /dev/null and b/test-data/slideshow/picture-transparency.pptx differ diff --git a/test-data/slideshow/table-with-different-font-colors.pptx b/test-data/slideshow/table-with-different-font-colors.pptx new file mode 100644 index 0000000000..6272f055eb Binary files /dev/null and b/test-data/slideshow/table-with-different-font-colors.pptx differ diff --git a/test-data/spreadsheet/54686_fraction_formats.txt b/test-data/spreadsheet/54686_fraction_formats.txt index 0de773efbd..c4b8528129 100644 --- a/test-data/spreadsheet/54686_fraction_formats.txt +++ b/test-data/spreadsheet/54686_fraction_formats.txt @@ -1,374 +1,374 @@ -Numerator Denominator Double 1Digit 2Digit 3Digit Half Quarter Eight Sixteenth Tenth 100th Tika-1132 -4051 8750153 0.000462963 0 0 0 0 0 0 0 0 0 4051/8750153 --105 100 -1.05 -1 -1 1/20 -1 1/20 -1 -1 -1 -1 1/16 -1 1/10 -1 5/100 -1 1/20 --104 100 -1.04 -1 -1 1/25 -1 1/25 -1 -1 -1 -1 1/16 -1 -1 4/100 -1 1/25 --103 100 -1.03 -1 -1 1/33 -1 3/100 -1 -1 -1 -1 -1 -1 3/100 -1 3/100 --102 100 -1.02 -1 -1 1/50 -1 1/50 -1 -1 -1 -1 -1 -1 2/100 -1 1/50 --101 100 -1.01 -1 -1 1/99 -1 1/100 -1 -1 -1 -1 -1 -1 1/100 -1 1/100 --100 100 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 --99 100 -0.99 -1 - 98/99 - 99/100 -1 -1 -1 -1 -1 - 99/100 - 99/100 --98 100 -0.98 -1 - 49/50 - 49/50 -1 -1 -1 -1 -1 - 98/100 - 49/50 --97 100 -0.97 -1 - 32/33 - 97/100 -1 -1 -1 -1 -1 - 97/100 - 97/100 --96 100 -0.96 -1 - 24/25 - 24/25 -1 -1 -1 - 15/16 -1 - 96/100 - 24/25 --95 100 -0.95 -1 - 19/20 - 19/20 -1 -1 -1 - 15/16 -1 - 95/100 - 19/20 --94 100 -0.94 -1 - 47/50 - 47/50 -1 -1 -1 - 15/16 - 9/10 - 94/100 - 47/50 --93 100 -0.93 -1 - 40/43 - 93/100 -1 -1 - 7/8 - 15/16 - 9/10 - 93/100 - 93/100 --92 100 -0.92 -1 - 23/25 - 23/25 -1 -1 - 7/8 - 15/16 - 9/10 - 92/100 - 23/25 --91 100 -0.91 -1 - 81/89 - 91/100 -1 -1 - 7/8 - 15/16 - 9/10 - 91/100 - 91/100 --90 100 -0.9 - 8/9 - 9/10 - 9/10 -1 -1 - 7/8 - 14/16 - 9/10 - 90/100 - 9/10 --89 100 -0.89 - 8/9 - 8/9 - 89/100 -1 -1 - 7/8 - 14/16 - 9/10 - 89/100 - 89/100 --88 100 -0.88 - 7/8 - 22/25 - 22/25 -1 -1 - 7/8 - 14/16 - 9/10 - 88/100 - 22/25 --87 100 -0.87 - 7/8 - 67/77 - 87/100 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 87/100 - 87/100 --86 100 -0.86 - 6/7 - 43/50 - 43/50 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 86/100 - 43/50 --85 100 -0.85 - 6/7 - 17/20 - 17/20 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 85/100 - 17/20 --84 100 -0.84 - 5/6 - 21/25 - 21/25 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 84/100 - 21/25 --83 100 -0.83 - 5/6 - 39/47 - 83/100 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 83/100 - 83/100 --82 100 -0.82 - 5/6 - 41/50 - 41/50 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 82/100 - 41/50 --81 100 -0.81 - 4/5 - 64/79 - 81/100 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 81/100 - 81/100 --80 100 -0.8 - 4/5 - 4/5 - 4/5 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 80/100 - 4/5 --79 100 -0.79 - 4/5 - 64/81 - 79/100 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 79/100 - 79/100 --78 100 -0.78 - 7/9 - 39/50 - 39/50 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 78/100 - 39/50 --77 100 -0.77 - 7/9 - 67/87 - 77/100 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 77/100 - 77/100 --76 100 -0.76 - 3/4 - 19/25 - 19/25 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 76/100 - 19/25 --75 100 -0.75 - 3/4 - 3/4 - 3/4 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 75/100 - 3/4 --74 100 -0.74 - 3/4 - 37/50 - 37/50 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 74/100 - 37/50 --73 100 -0.73 - 3/4 - 46/63 - 73/100 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 73/100 - 73/100 --72 100 -0.72 - 5/7 - 18/25 - 18/25 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 72/100 - 18/25 --71 100 -0.71 - 5/7 - 22/31 - 71/100 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 71/100 - 71/100 --70 100 -0.7 - 2/3 - 7/10 - 7/10 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 70/100 - 7/10 --69 100 -0.69 - 2/3 - 20/29 - 69/100 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 69/100 - 69/100 --68 100 -0.68 - 2/3 - 17/25 - 17/25 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 68/100 - 17/25 --67 100 -0.67 - 2/3 - 65/97 - 67/100 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 67/100 - 67/100 --66 100 -0.66 - 2/3 - 33/50 - 33/50 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 66/100 - 33/50 --65 100 -0.65 - 2/3 - 13/20 - 13/20 - 1/2 - 3/4 - 5/8 - 10/16 - 7/10 - 65/100 - 13/20 --64 100 -0.64 - 2/3 - 16/25 - 16/25 - 1/2 - 3/4 - 5/8 - 10/16 - 6/10 - 64/100 - 16/25 --63 100 -0.63 - 5/8 - 46/73 - 63/100 - 1/2 - 3/4 - 5/8 - 10/16 - 6/10 - 63/100 - 63/100 --62 100 -0.62 - 5/8 - 31/50 - 31/50 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 62/100 - 31/50 --61 100 -0.61 - 3/5 - 36/59 - 61/100 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 61/100 - 61/100 --60 100 -0.6 - 3/5 - 3/5 - 3/5 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 60/100 - 3/5 --59 100 -0.59 - 3/5 - 23/39 - 59/100 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 59/100 - 59/100 --58 100 -0.58 - 4/7 - 29/50 - 29/50 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 58/100 - 29/50 --57 100 -0.57 - 4/7 - 53/93 - 57/100 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 57/100 - 57/100 --56 100 -0.56 - 5/9 - 14/25 - 14/25 - 1/2 - 2/4 - 4/8 - 9/16 - 6/10 - 56/100 - 14/25 --55 100 -0.55 - 5/9 - 11/20 - 11/20 - 1/2 - 2/4 - 4/8 - 9/16 - 6/10 - 55/100 - 11/20 --54 100 -0.54 - 1/2 - 27/50 - 27/50 - 1/2 - 2/4 - 4/8 - 9/16 - 5/10 - 54/100 - 27/50 --53 100 -0.53 - 1/2 - 44/83 - 53/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 53/100 - 53/100 --52 100 -0.52 - 1/2 - 13/25 - 13/25 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 52/100 - 13/25 --51 100 -0.51 - 1/2 - 25/49 - 51/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 51/100 - 51/100 --50 100 -0.5 - 1/2 - 1/2 - 1/2 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 50/100 - 1/2 --49 100 -0.49 - 1/2 - 24/49 - 49/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 49/100 - 49/100 --48 100 -0.48 - 1/2 - 12/25 - 12/25 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 48/100 - 12/25 --47 100 -0.47 - 1/2 - 8/17 - 47/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 47/100 - 47/100 --46 100 -0.46 - 1/2 - 23/50 - 23/50 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 46/100 - 23/50 --45 100 -0.45 - 4/9 - 9/20 - 9/20 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 45/100 - 9/20 --44 100 -0.44 - 4/9 - 11/25 - 11/25 - 1/2 - 2/4 - 4/8 - 7/16 - 4/10 - 44/100 - 11/25 --43 100 -0.43 - 3/7 - 3/7 - 43/100 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 43/100 - 43/100 --42 100 -0.42 - 3/7 - 21/50 - 21/50 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 42/100 - 21/50 --41 100 -0.41 - 2/5 - 16/39 - 41/100 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 41/100 - 41/100 --40 100 -0.4 - 2/5 - 2/5 - 2/5 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 40/100 - 2/5 --39 100 -0.39 - 2/5 - 16/41 - 39/100 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 39/100 - 39/100 --38 100 -0.38 - 3/8 - 19/50 - 19/50 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 38/100 - 19/50 --37 100 -0.37 - 3/8 - 10/27 - 37/100 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 37/100 - 37/100 --36 100 -0.36 - 1/3 - 9/25 - 9/25 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 36/100 - 9/25 --35 100 -0.35 - 1/3 - 7/20 - 7/20 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 35/100 - 7/20 --34 100 -0.34 - 1/3 - 17/50 - 17/50 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 34/100 - 17/50 --33 100 -0.33 - 1/3 - 1/3 - 33/100 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 33/100 - 33/100 --32 100 -0.32 - 1/3 - 8/25 - 8/25 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 32/100 - 8/25 --31 100 -0.31 - 1/3 - 22/71 - 31/100 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 31/100 - 31/100 --30 100 -0.3 - 2/7 - 3/10 - 3/10 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 30/100 - 3/10 --29 100 -0.29 - 2/7 - 20/69 - 29/100 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 29/100 - 29/100 --28 100 -0.28 - 2/7 - 7/25 - 7/25 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 28/100 - 7/25 --27 100 -0.27 - 1/4 - 10/37 - 27/100 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 27/100 - 27/100 --26 100 -0.26 - 1/4 - 13/50 - 13/50 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 26/100 - 13/50 --25 100 -0.25 - 1/4 - 1/4 - 1/4 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 25/100 - 1/4 --24 100 -0.24 - 1/4 - 6/25 - 6/25 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 24/100 - 6/25 --23 100 -0.23 - 2/9 - 3/13 - 23/100 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 23/100 - 23/100 --22 100 -0.22 - 2/9 - 11/50 - 11/50 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 22/100 - 11/50 --21 100 -0.21 - 1/5 - 17/81 - 21/100 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 21/100 - 21/100 --20 100 -0.2 - 1/5 - 1/5 - 1/5 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 20/100 - 1/5 --19 100 -0.19 - 1/5 - 15/79 - 19/100 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 19/100 - 19/100 --18 100 -0.18 - 1/6 - 9/50 - 9/50 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 18/100 - 9/50 --17 100 -0.17 - 1/6 - 8/47 - 17/100 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 17/100 - 17/100 --16 100 -0.16 - 1/6 - 4/25 - 4/25 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 16/100 - 4/25 --15 100 -0.15 - 1/7 - 3/20 - 3/20 -0 - 1/4 - 1/8 - 2/16 - 2/10 - 15/100 - 3/20 --14 100 -0.14 - 1/7 - 7/50 - 7/50 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 14/100 - 7/50 --13 100 -0.13 - 1/8 - 3/23 - 13/100 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 13/100 - 13/100 --12 100 -0.12 - 1/8 - 3/25 - 3/25 -0 -0 - 1/8 - 2/16 - 1/10 - 12/100 - 3/25 --11 100 -0.11 - 1/9 - 10/91 - 11/100 -0 -0 - 1/8 - 2/16 - 1/10 - 11/100 - 11/100 --10 100 -0.1 -0 - 1/10 - 1/10 -0 -0 - 1/8 - 2/16 - 1/10 - 10/100 - 1/10 --9 100 -0.09 -0 - 1/11 - 9/100 -0 -0 - 1/8 - 1/16 - 1/10 - 9/100 - 9/100 --8 100 -0.08 -0 - 2/25 - 2/25 -0 -0 - 1/8 - 1/16 - 1/10 - 8/100 - 2/25 --7 100 -0.07 -0 - 4/57 - 7/100 -0 -0 - 1/8 - 1/16 - 1/10 - 7/100 - 7/100 --6 100 -0.06 -0 - 3/50 - 3/50 -0 -0 -0 - 1/16 - 1/10 - 6/100 - 3/50 --5 100 -0.05 -0 - 1/20 - 1/20 -0 -0 -0 - 1/16 - 1/10 - 5/100 - 1/20 --4 100 -0.04 -0 - 1/25 - 1/25 -0 -0 -0 - 1/16 -0 - 4/100 - 1/25 --3 100 -0.03 -0 - 2/67 - 3/100 -0 -0 -0 -0 -0 - 3/100 - 3/100 --2 100 -0.02 -0 - 1/50 - 1/50 -0 -0 -0 -0 -0 - 2/100 - 1/50 --1 100 -0.01 -0 -0 - 1/100 -0 -0 -0 -0 -0 - 1/100 - 1/100 -0 100 0 0 0 0 0 0 0 0 0 0 0 -1 100 0.01 0 0 1/100 0 0 0 0 0 1/100 1/100 -2 100 0.02 0 1/50 1/50 0 0 0 0 0 2/100 1/50 -3 100 0.03 0 2/67 3/100 0 0 0 0 0 3/100 3/100 -4 100 0.04 0 1/25 1/25 0 0 0 1/16 0 4/100 1/25 -5 100 0.05 0 1/20 1/20 0 0 0 1/16 1/10 5/100 1/20 -6 100 0.06 0 3/50 3/50 0 0 0 1/16 1/10 6/100 3/50 -7 100 0.07 0 4/57 7/100 0 0 1/8 1/16 1/10 7/100 7/100 -8 100 0.08 0 2/25 2/25 0 0 1/8 1/16 1/10 8/100 2/25 -9 100 0.09 0 1/11 9/100 0 0 1/8 1/16 1/10 9/100 9/100 -10 100 0.1 0 1/10 1/10 0 0 1/8 2/16 1/10 10/100 1/10 -11 100 0.11 1/9 10/91 11/100 0 0 1/8 2/16 1/10 11/100 11/100 -12 100 0.12 1/8 3/25 3/25 0 0 1/8 2/16 1/10 12/100 3/25 -13 100 0.13 1/8 3/23 13/100 0 1/4 1/8 2/16 1/10 13/100 13/100 -14 100 0.14 1/7 7/50 7/50 0 1/4 1/8 2/16 1/10 14/100 7/50 -15 100 0.15 1/7 3/20 3/20 0 1/4 1/8 2/16 2/10 15/100 3/20 -16 100 0.16 1/6 4/25 4/25 0 1/4 1/8 3/16 2/10 16/100 4/25 -17 100 0.17 1/6 8/47 17/100 0 1/4 1/8 3/16 2/10 17/100 17/100 -18 100 0.18 1/6 9/50 9/50 0 1/4 1/8 3/16 2/10 18/100 9/50 -19 100 0.19 1/5 15/79 19/100 0 1/4 2/8 3/16 2/10 19/100 19/100 -20 100 0.2 1/5 1/5 1/5 0 1/4 2/8 3/16 2/10 20/100 1/5 -21 100 0.21 1/5 17/81 21/100 0 1/4 2/8 3/16 2/10 21/100 21/100 -22 100 0.22 2/9 11/50 11/50 0 1/4 2/8 4/16 2/10 22/100 11/50 -23 100 0.23 2/9 3/13 23/100 0 1/4 2/8 4/16 2/10 23/100 23/100 -24 100 0.24 1/4 6/25 6/25 0 1/4 2/8 4/16 2/10 24/100 6/25 -25 100 0.25 1/4 1/4 1/4 1/2 1/4 2/8 4/16 3/10 25/100 1/4 -26 100 0.26 1/4 13/50 13/50 1/2 1/4 2/8 4/16 3/10 26/100 13/50 -27 100 0.27 1/4 10/37 27/100 1/2 1/4 2/8 4/16 3/10 27/100 27/100 -28 100 0.28 2/7 7/25 7/25 1/2 1/4 2/8 4/16 3/10 28/100 7/25 -29 100 0.29 2/7 20/69 29/100 1/2 1/4 2/8 5/16 3/10 29/100 29/100 -30 100 0.3 2/7 3/10 3/10 1/2 1/4 2/8 5/16 3/10 30/100 3/10 -31 100 0.31 1/3 22/71 31/100 1/2 1/4 2/8 5/16 3/10 31/100 31/100 -32 100 0.32 1/3 8/25 8/25 1/2 1/4 3/8 5/16 3/10 32/100 8/25 -33 100 0.33 1/3 1/3 33/100 1/2 1/4 3/8 5/16 3/10 33/100 33/100 -34 100 0.34 1/3 17/50 17/50 1/2 1/4 3/8 5/16 3/10 34/100 17/50 -35 100 0.35 1/3 7/20 7/20 1/2 1/4 3/8 6/16 4/10 35/100 7/20 -36 100 0.36 1/3 9/25 9/25 1/2 1/4 3/8 6/16 4/10 36/100 9/25 -37 100 0.37 3/8 10/27 37/100 1/2 1/4 3/8 6/16 4/10 37/100 37/100 -38 100 0.38 3/8 19/50 19/50 1/2 2/4 3/8 6/16 4/10 38/100 19/50 -39 100 0.39 2/5 16/41 39/100 1/2 2/4 3/8 6/16 4/10 39/100 39/100 -40 100 0.4 2/5 2/5 2/5 1/2 2/4 3/8 6/16 4/10 40/100 2/5 -41 100 0.41 2/5 16/39 41/100 1/2 2/4 3/8 7/16 4/10 41/100 41/100 -42 100 0.42 3/7 21/50 21/50 1/2 2/4 3/8 7/16 4/10 42/100 21/50 -43 100 0.43 3/7 3/7 43/100 1/2 2/4 3/8 7/16 4/10 43/100 43/100 -44 100 0.44 4/9 11/25 11/25 1/2 2/4 4/8 7/16 4/10 44/100 11/25 -45 100 0.45 4/9 9/20 9/20 1/2 2/4 4/8 7/16 5/10 45/100 9/20 -46 100 0.46 1/2 23/50 23/50 1/2 2/4 4/8 7/16 5/10 46/100 23/50 -47 100 0.47 1/2 8/17 47/100 1/2 2/4 4/8 8/16 5/10 47/100 47/100 -48 100 0.48 1/2 12/25 12/25 1/2 2/4 4/8 8/16 5/10 48/100 12/25 -49 100 0.49 1/2 24/49 49/100 1/2 2/4 4/8 8/16 5/10 49/100 49/100 -50 100 0.5 1/2 1/2 1/2 1/2 2/4 4/8 8/16 5/10 50/100 1/2 -51 100 0.51 1/2 25/49 51/100 1/2 2/4 4/8 8/16 5/10 51/100 51/100 -52 100 0.52 1/2 13/25 13/25 1/2 2/4 4/8 8/16 5/10 52/100 13/25 -53 100 0.53 1/2 44/83 53/100 1/2 2/4 4/8 8/16 5/10 53/100 53/100 -54 100 0.54 1/2 27/50 27/50 1/2 2/4 4/8 9/16 5/10 54/100 27/50 -55 100 0.55 5/9 11/20 11/20 1/2 2/4 4/8 9/16 6/10 55/100 11/20 -56 100 0.56 5/9 14/25 14/25 1/2 2/4 4/8 9/16 6/10 56/100 14/25 -57 100 0.57 4/7 53/93 57/100 1/2 2/4 5/8 9/16 6/10 57/100 57/100 -58 100 0.58 4/7 29/50 29/50 1/2 2/4 5/8 9/16 6/10 58/100 29/50 -59 100 0.59 3/5 23/39 59/100 1/2 2/4 5/8 9/16 6/10 59/100 59/100 -60 100 0.6 3/5 3/5 3/5 1/2 2/4 5/8 10/16 6/10 60/100 3/5 -61 100 0.61 3/5 36/59 61/100 1/2 2/4 5/8 10/16 6/10 61/100 61/100 -62 100 0.62 5/8 31/50 31/50 1/2 2/4 5/8 10/16 6/10 62/100 31/50 -63 100 0.63 5/8 46/73 63/100 1/2 3/4 5/8 10/16 6/10 63/100 63/100 -64 100 0.64 2/3 16/25 16/25 1/2 3/4 5/8 10/16 6/10 64/100 16/25 -65 100 0.65 2/3 13/20 13/20 1/2 3/4 5/8 10/16 7/10 65/100 13/20 -66 100 0.66 2/3 33/50 33/50 1/2 3/4 5/8 11/16 7/10 66/100 33/50 -67 100 0.67 2/3 65/97 67/100 1/2 3/4 5/8 11/16 7/10 67/100 67/100 -68 100 0.68 2/3 17/25 17/25 1/2 3/4 5/8 11/16 7/10 68/100 17/25 -69 100 0.69 2/3 20/29 69/100 1/2 3/4 6/8 11/16 7/10 69/100 69/100 -70 100 0.7 2/3 7/10 7/10 1/2 3/4 6/8 11/16 7/10 70/100 7/10 -71 100 0.71 5/7 22/31 71/100 1/2 3/4 6/8 11/16 7/10 71/100 71/100 -72 100 0.72 5/7 18/25 18/25 1/2 3/4 6/8 12/16 7/10 72/100 18/25 -73 100 0.73 3/4 46/63 73/100 1/2 3/4 6/8 12/16 7/10 73/100 73/100 -74 100 0.74 3/4 37/50 37/50 1/2 3/4 6/8 12/16 7/10 74/100 37/50 -75 100 0.75 3/4 3/4 3/4 1 3/4 6/8 12/16 8/10 75/100 3/4 -76 100 0.76 3/4 19/25 19/25 1 3/4 6/8 12/16 8/10 76/100 19/25 -77 100 0.77 7/9 67/87 77/100 1 3/4 6/8 12/16 8/10 77/100 77/100 -78 100 0.78 7/9 39/50 39/50 1 3/4 6/8 12/16 8/10 78/100 39/50 -79 100 0.79 4/5 64/81 79/100 1 3/4 6/8 13/16 8/10 79/100 79/100 -80 100 0.8 4/5 4/5 4/5 1 3/4 6/8 13/16 8/10 80/100 4/5 -81 100 0.81 4/5 64/79 81/100 1 3/4 6/8 13/16 8/10 81/100 81/100 -82 100 0.82 5/6 41/50 41/50 1 3/4 7/8 13/16 8/10 82/100 41/50 -83 100 0.83 5/6 39/47 83/100 1 3/4 7/8 13/16 8/10 83/100 83/100 -84 100 0.84 5/6 21/25 21/25 1 3/4 7/8 13/16 8/10 84/100 21/25 -85 100 0.85 6/7 17/20 17/20 1 3/4 7/8 14/16 9/10 85/100 17/20 -86 100 0.86 6/7 43/50 43/50 1 3/4 7/8 14/16 9/10 86/100 43/50 -87 100 0.87 7/8 67/77 87/100 1 3/4 7/8 14/16 9/10 87/100 87/100 -88 100 0.88 7/8 22/25 22/25 1 1 7/8 14/16 9/10 88/100 22/25 -89 100 0.89 8/9 8/9 89/100 1 1 7/8 14/16 9/10 89/100 89/100 -90 100 0.9 8/9 9/10 9/10 1 1 7/8 14/16 9/10 90/100 9/10 -91 100 0.91 1 81/89 91/100 1 1 7/8 15/16 9/10 91/100 91/100 -92 100 0.92 1 23/25 23/25 1 1 7/8 15/16 9/10 92/100 23/25 -93 100 0.93 1 40/43 93/100 1 1 7/8 15/16 9/10 93/100 93/100 -94 100 0.94 1 47/50 47/50 1 1 1 15/16 9/10 94/100 47/50 -95 100 0.95 1 19/20 19/20 1 1 1 15/16 1 95/100 19/20 -96 100 0.96 1 24/25 24/25 1 1 1 15/16 1 96/100 24/25 -97 100 0.97 1 32/33 97/100 1 1 1 1 1 97/100 97/100 -98 100 0.98 1 49/50 49/50 1 1 1 1 1 98/100 49/50 -99 100 0.99 1 98/99 99/100 1 1 1 1 1 99/100 99/100 -100 100 1 1 1 1 1 1 1 1 1 1 1 -101 100 1.01 1 1 1/99 1 1/100 1 1 1 1 1 1 1/100 1 1/100 -102 100 1.02 1 1 1/50 1 1/50 1 1 1 1 1 1 2/100 1 1/50 -103 100 1.03 1 1 1/33 1 3/100 1 1 1 1 1 1 3/100 1 3/100 -104 100 1.04 1 1 1/25 1 1/25 1 1 1 1 1/16 1 1 4/100 1 1/25 -105 100 1.05 1 1 1/20 1 1/20 1 1 1 1 1/16 1 1/10 1 5/100 1 1/20 -106 100 1.06 1 1 3/50 1 3/50 1 1 1 1 1/16 1 1/10 1 6/100 1 3/50 -107 100 1.07 1 1 4/57 1 7/100 1 1 1 1/8 1 1/16 1 1/10 1 7/100 1 7/100 -108 100 1.08 1 1 2/25 1 2/25 1 1 1 1/8 1 1/16 1 1/10 1 8/100 1 2/25 -109 100 1.09 1 1 1/11 1 9/100 1 1 1 1/8 1 1/16 1 1/10 1 9/100 1 9/100 -110 100 1.1 1 1/9 1 1/10 1 1/10 1 1 1 1/8 1 2/16 1 1/10 1 10/100 1 1/10 -111 100 1.11 1 1/9 1 1/9 1 11/100 1 1 1 1/8 1 2/16 1 1/10 1 11/100 1 11/100 -112 100 1.12 1 1/8 1 3/25 1 3/25 1 1 1 1/8 1 2/16 1 1/10 1 12/100 1 3/25 -113 100 1.13 1 1/8 1 10/77 1 13/100 1 1 1/4 1 1/8 1 2/16 1 1/10 1 13/100 1 13/100 -114 100 1.14 1 1/7 1 7/50 1 7/50 1 1 1/4 1 1/8 1 2/16 1 1/10 1 14/100 1 7/50 -115 100 1.15 1 1/7 1 3/20 1 3/20 1 1 1/4 1 1/8 1 2/16 1 1/10 1 15/100 1 3/20 -116 100 1.16 1 1/6 1 4/25 1 4/25 1 1 1/4 1 1/8 1 3/16 1 2/10 1 16/100 1 4/25 -117 100 1.17 1 1/6 1 9/53 1 17/100 1 1 1/4 1 1/8 1 3/16 1 2/10 1 17/100 1 17/100 -118 100 1.18 1 1/6 1 9/50 1 9/50 1 1 1/4 1 1/8 1 3/16 1 2/10 1 18/100 1 9/50 -119 100 1.19 1 1/5 1 15/79 1 19/100 1 1 1/4 1 2/8 1 3/16 1 2/10 1 19/100 1 19/100 -120 100 1.2 1 1/5 1 1/5 1 1/5 1 1 1/4 1 2/8 1 3/16 1 2/10 1 20/100 1 1/5 -121 100 1.21 1 1/5 1 17/81 1 21/100 1 1 1/4 1 2/8 1 3/16 1 2/10 1 21/100 1 21/100 -122 100 1.22 1 2/9 1 11/50 1 11/50 1 1 1/4 1 2/8 1 4/16 1 2/10 1 22/100 1 11/50 -123 100 1.23 1 2/9 1 20/87 1 23/100 1 1 1/4 1 2/8 1 4/16 1 2/10 1 23/100 1 23/100 -124 100 1.24 1 1/4 1 6/25 1 6/25 1 1 1/4 1 2/8 1 4/16 1 2/10 1 24/100 1 6/25 -125 100 1.25 1 1/4 1 1/4 1 1/4 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 25/100 1 1/4 -126 100 1.26 1 1/4 1 13/50 1 13/50 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 26/100 1 13/50 -127 100 1.27 1 1/4 1 10/37 1 27/100 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 27/100 1 27/100 --103 201 -0.512437811 - 1/2 - 41/80 - 103/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 51/100 - 103/201 --100 201 -0.497512438 - 1/2 - 1/2 - 100/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 50/100 - 100/201 --97 201 -0.482587065 - 1/2 - 14/29 - 97/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 48/100 - 97/201 --94 201 -0.467661692 - 1/2 - 29/62 - 94/201 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 47/100 - 94/201 --91 201 -0.452736318 - 4/9 - 24/53 - 91/201 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 45/100 - 91/201 --88 201 -0.437810945 - 4/9 - 7/16 - 88/201 - 1/2 - 2/4 - 4/8 - 7/16 - 4/10 - 44/100 - 88/201 --85 201 -0.422885572 - 3/7 - 11/26 - 85/201 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 42/100 - 85/201 --82 201 -0.407960199 - 2/5 - 31/76 - 82/201 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 41/100 - 82/201 --79 201 -0.393034826 - 2/5 - 11/28 - 79/201 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 39/100 - 79/201 --76 201 -0.378109453 - 3/8 - 31/82 - 76/201 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 38/100 - 76/201 --73 201 -0.36318408 - 1/3 - 4/11 - 73/201 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 36/100 - 73/201 --70 201 -0.348258706 - 1/3 - 31/89 - 70/201 - 1/2 - 1/4 - 3/8 - 6/16 - 3/10 - 35/100 - 70/201 --67 201 -0.333333333 - 1/3 - 1/3 - 1/3 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 33/100 - 1/3 --64 201 -0.31840796 - 1/3 - 7/22 - 64/201 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 32/100 - 64/201 --61 201 -0.303482587 - 1/3 - 17/56 - 61/201 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 30/100 - 61/201 --58 201 -0.288557214 - 2/7 - 15/52 - 58/201 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 29/100 - 58/201 --55 201 -0.273631841 - 2/7 - 26/95 - 55/201 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 27/100 - 55/201 --52 201 -0.258706468 - 1/4 - 15/58 - 52/201 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 26/100 - 52/201 --49 201 -0.243781095 - 1/4 - 10/41 - 49/201 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 24/100 - 49/201 --46 201 -0.228855721 - 2/9 - 19/83 - 46/201 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 23/100 - 46/201 --43 201 -0.213930348 - 1/5 - 3/14 - 43/201 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 21/100 - 43/201 --40 201 -0.199004975 - 1/5 - 1/5 - 40/201 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 20/100 - 40/201 --37 201 -0.184079602 - 1/5 - 7/38 - 37/201 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 18/100 - 37/201 --34 201 -0.169154229 - 1/6 - 11/65 - 34/201 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 17/100 - 34/201 --31 201 -0.154228856 - 1/6 - 2/13 - 31/201 -0 - 1/4 - 1/8 - 2/16 - 2/10 - 15/100 - 31/201 --28 201 -0.139303483 - 1/7 - 11/79 - 28/201 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 14/100 - 28/201 --25 201 -0.124378109 - 1/8 - 1/8 - 25/201 -0 -0 - 1/8 - 2/16 - 1/10 - 12/100 - 25/201 --22 201 -0.109452736 - 1/9 - 7/64 - 22/201 -0 -0 - 1/8 - 2/16 - 1/10 - 11/100 - 22/201 --19 201 -0.094527363 -0 - 7/74 - 19/201 -0 -0 - 1/8 - 2/16 - 1/10 - 9/100 - 19/201 --16 201 -0.07960199 -0 - 7/88 - 16/201 -0 -0 - 1/8 - 1/16 - 1/10 - 8/100 - 16/201 --13 201 -0.064676617 -0 - 2/31 - 13/201 -0 -0 - 1/8 - 1/16 - 1/10 - 6/100 - 13/201 --10 201 -0.049751244 -0 - 1/20 - 10/201 -0 -0 -0 - 1/16 -0 - 5/100 - 10/201 --7 201 -0.034825871 -0 - 3/86 - 7/201 -0 -0 -0 - 1/16 -0 - 3/100 - 7/201 --4 201 -0.019900498 -0 - 1/50 - 4/201 -0 -0 -0 -0 -0 - 2/100 - 4/201 --1 201 -0.004975124 -0 -0 - 1/201 -0 -0 -0 -0 -0 -0 - 1/201 -2 201 0.009950249 0 0 2/201 0 0 0 0 0 1/100 2/201 -5 201 0.024875622 0 1/40 5/201 0 0 0 0 0 2/100 5/201 -8 201 0.039800995 0 1/25 8/201 0 0 0 1/16 0 4/100 8/201 -11 201 0.054726368 0 4/73 11/201 0 0 0 1/16 1/10 5/100 11/201 -14 201 0.069651741 0 3/43 14/201 0 0 1/8 1/16 1/10 7/100 14/201 -17 201 0.084577114 0 6/71 17/201 0 0 1/8 1/16 1/10 8/100 17/201 -20 201 0.099502488 0 1/10 20/201 0 0 1/8 2/16 1/10 10/100 20/201 -23 201 0.114427861 1/9 4/35 23/201 0 0 1/8 2/16 1/10 11/100 23/201 -26 201 0.129353234 1/8 11/85 26/201 0 1/4 1/8 2/16 1/10 13/100 26/201 -29 201 0.144278607 1/7 14/97 29/201 0 1/4 1/8 2/16 1/10 14/100 29/201 -32 201 0.15920398 1/6 7/44 32/201 0 1/4 1/8 3/16 2/10 16/100 32/201 -35 201 0.174129353 1/6 4/23 35/201 0 1/4 1/8 3/16 2/10 17/100 35/201 -38 201 0.189054726 1/5 7/37 38/201 0 1/4 2/8 3/16 2/10 19/100 38/201 -41 201 0.2039801 1/5 10/49 41/201 0 1/4 2/8 3/16 2/10 20/100 41/201 -44 201 0.218905473 2/9 7/32 44/201 0 1/4 2/8 4/16 2/10 22/100 44/201 -47 201 0.233830846 1/4 18/77 47/201 0 1/4 2/8 4/16 2/10 23/100 47/201 -50 201 0.248756219 1/4 1/4 50/201 0 1/4 2/8 4/16 2/10 25/100 50/201 -53 201 0.263681592 1/4 24/91 53/201 1/2 1/4 2/8 4/16 3/10 26/100 53/201 -56 201 0.278606965 2/7 17/61 56/201 1/2 1/4 2/8 4/16 3/10 28/100 56/201 -59 201 0.293532338 2/7 27/92 59/201 1/2 1/4 2/8 5/16 3/10 29/100 59/201 -62 201 0.308457711 1/3 29/94 62/201 1/2 1/4 2/8 5/16 3/10 31/100 62/201 -65 201 0.323383085 1/3 11/34 65/201 1/2 1/4 3/8 5/16 3/10 32/100 65/201 -68 201 0.338308458 1/3 23/68 68/201 1/2 1/4 3/8 5/16 3/10 34/100 68/201 -71 201 0.353233831 1/3 6/17 71/201 1/2 1/4 3/8 6/16 4/10 35/100 71/201 -74 201 0.368159204 3/8 7/19 74/201 1/2 1/4 3/8 6/16 4/10 37/100 74/201 -77 201 0.383084577 3/8 18/47 77/201 1/2 2/4 3/8 6/16 4/10 38/100 77/201 -80 201 0.39800995 2/5 39/98 80/201 1/2 2/4 3/8 6/16 4/10 40/100 80/201 -83 201 0.412935323 2/5 19/46 83/201 1/2 2/4 3/8 7/16 4/10 41/100 83/201 -86 201 0.427860697 3/7 3/7 86/201 1/2 2/4 3/8 7/16 4/10 43/100 86/201 -89 201 0.44278607 4/9 31/70 89/201 1/2 2/4 4/8 7/16 4/10 44/100 89/201 -92 201 0.457711443 1/2 27/59 92/201 1/2 2/4 4/8 7/16 5/10 46/100 92/201 -95 201 0.472636816 1/2 26/55 95/201 1/2 2/4 4/8 8/16 5/10 47/100 95/201 -98 201 0.487562189 1/2 39/80 98/201 1/2 2/4 4/8 8/16 5/10 49/100 98/201 -101 201 0.502487562 1/2 1/2 101/201 1/2 2/4 4/8 8/16 5/10 50/100 101/201 -104 201 0.517412935 1/2 15/29 104/201 1/2 2/4 4/8 8/16 5/10 52/100 104/201 -107 201 0.532338308 1/2 33/62 107/201 1/2 2/4 4/8 9/16 5/10 53/100 107/201 -110 201 0.547263682 5/9 29/53 110/201 1/2 2/4 4/8 9/16 5/10 55/100 110/201 -113 201 0.562189055 5/9 9/16 113/201 1/2 2/4 4/8 9/16 6/10 56/100 113/201 -116 201 0.577114428 4/7 15/26 116/201 1/2 2/4 5/8 9/16 6/10 58/100 116/201 -119 201 0.592039801 3/5 45/76 119/201 1/2 2/4 5/8 9/16 6/10 59/100 119/201 -122 201 0.606965174 3/5 17/28 122/201 1/2 2/4 5/8 10/16 6/10 61/100 122/201 -125 201 0.621890547 5/8 51/82 125/201 1/2 2/4 5/8 10/16 6/10 62/100 125/201 -128 201 0.63681592 2/3 7/11 128/201 1/2 3/4 5/8 10/16 6/10 64/100 128/201 -131 201 0.651741294 2/3 58/89 131/201 1/2 3/4 5/8 10/16 7/10 65/100 131/201 -134 201 0.666666667 2/3 2/3 2/3 1/2 3/4 5/8 11/16 7/10 67/100 2/3 -137 201 0.68159204 2/3 15/22 137/201 1/2 3/4 5/8 11/16 7/10 68/100 137/201 -140 201 0.696517413 2/3 39/56 140/201 1/2 3/4 6/8 11/16 7/10 70/100 140/201 -143 201 0.711442786 5/7 37/52 143/201 1/2 3/4 6/8 11/16 7/10 71/100 143/201 -146 201 0.726368159 5/7 69/95 146/201 1/2 3/4 6/8 12/16 7/10 73/100 146/201 -149 201 0.741293532 3/4 43/58 149/201 1/2 3/4 6/8 12/16 7/10 74/100 149/201 -152 201 0.756218905 3/4 31/41 152/201 1 3/4 6/8 12/16 8/10 76/100 152/201 -155 201 0.771144279 7/9 64/83 155/201 1 3/4 6/8 12/16 8/10 77/100 155/201 -158 201 0.786069652 4/5 11/14 158/201 1 3/4 6/8 13/16 8/10 79/100 158/201 -161 201 0.800995025 4/5 4/5 161/201 1 3/4 6/8 13/16 8/10 80/100 161/201 -164 201 0.815920398 4/5 31/38 164/201 1 3/4 7/8 13/16 8/10 82/100 164/201 -167 201 0.830845771 5/6 54/65 167/201 1 3/4 7/8 13/16 8/10 83/100 167/201 -170 201 0.845771144 5/6 11/13 170/201 1 3/4 7/8 14/16 8/10 85/100 170/201 -173 201 0.860696517 6/7 68/79 173/201 1 3/4 7/8 14/16 9/10 86/100 173/201 -176 201 0.875621891 7/8 7/8 176/201 1 1 7/8 14/16 9/10 88/100 176/201 -179 201 0.890547264 8/9 57/64 179/201 1 1 7/8 14/16 9/10 89/100 179/201 -182 201 0.905472637 1 67/74 182/201 1 1 7/8 14/16 9/10 91/100 182/201 -185 201 0.92039801 1 81/88 185/201 1 1 7/8 15/16 9/10 92/100 185/201 -188 201 0.935323383 1 29/31 188/201 1 1 7/8 15/16 9/10 94/100 188/201 -191 201 0.950248756 1 19/20 191/201 1 1 1 15/16 1 95/100 191/201 -194 201 0.965174129 1 83/86 194/201 1 1 1 15/16 1 97/100 194/201 -197 201 0.980099502 1 49/50 197/201 1 1 1 1 1 98/100 197/201 -200 201 0.995024876 1 1 200/201 1 1 1 1 1 1 200/201 -203 201 1.009950249 1 1 1 2/201 1 1 1 1 1 1 1/100 1 2/201 -206 201 1.024875622 1 1 1/40 1 5/201 1 1 1 1 1 1 2/100 1 5/201 -209 201 1.039800995 1 1 1/25 1 8/201 1 1 1 1 1/16 1 1 4/100 1 8/201 -212 201 1.054726368 1 1 4/73 1 11/201 1 1 1 1 1/16 1 1/10 1 5/100 1 11/201 -215 201 1.069651741 1 1 3/43 1 14/201 1 1 1 1/8 1 1/16 1 1/10 1 7/100 1 14/201 -218 201 1.084577114 1 1 6/71 1 17/201 1 1 1 1/8 1 1/16 1 1/10 1 8/100 1 17/201 -221 201 1.099502488 1 1 1/10 1 20/201 1 1 1 1/8 1 2/16 1 1/10 1 10/100 1 20/201 -224 201 1.114427861 1 1/9 1 4/35 1 23/201 1 1 1 1/8 1 2/16 1 1/10 1 11/100 1 23/201 -227 201 1.129353234 1 1/8 1 11/85 1 26/201 1 1 1/4 1 1/8 1 2/16 1 1/10 1 13/100 1 26/201 -230 201 1.144278607 1 1/7 1 14/97 1 29/201 1 1 1/4 1 1/8 1 2/16 1 1/10 1 14/100 1 29/201 -233 201 1.15920398 1 1/6 1 7/44 1 32/201 1 1 1/4 1 1/8 1 3/16 1 2/10 1 16/100 1 32/201 -236 201 1.174129353 1 1/6 1 4/23 1 35/201 1 1 1/4 1 1/8 1 3/16 1 2/10 1 17/100 1 35/201 -239 201 1.189054726 1 1/5 1 7/37 1 38/201 1 1 1/4 1 2/8 1 3/16 1 2/10 1 19/100 1 38/201 -242 201 1.2039801 1 1/5 1 10/49 1 41/201 1 1 1/4 1 2/8 1 3/16 1 2/10 1 20/100 1 41/201 -245 201 1.218905473 1 2/9 1 7/32 1 44/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 22/100 1 44/201 -248 201 1.233830846 1 1/4 1 18/77 1 47/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 23/100 1 47/201 -251 201 1.248756219 1 1/4 1 1/4 1 50/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 25/100 1 50/201 -254 201 1.263681592 1 1/4 1 24/91 1 53/201 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 26/100 1 53/201 - - - - - - - - - - - - - - - - - - - +Numerator Denominator Double 1Digit 2Digit 3Digit Half Quarter Eight Sixteenth Tenth 100th Tika-1132 +4051 8750153 0.000462963 0 0 0 0 0 0 0 0 0 4051/8750153 +-105 100 -1.05 -1 -1 1/20 -1 1/20 -1 -1 -1 -1 1/16 -1 1/10 -1 5/100 -1 1/20 +-104 100 -1.04 -1 -1 1/25 -1 1/25 -1 -1 -1 -1 1/16 -1 -1 4/100 -1 1/25 +-103 100 -1.03 -1 -1 1/33 -1 3/100 -1 -1 -1 -1 -1 -1 3/100 -1 3/100 +-102 100 -1.02 -1 -1 1/50 -1 1/50 -1 -1 -1 -1 -1 -1 2/100 -1 1/50 +-101 100 -1.01 -1 -1 1/99 -1 1/100 -1 -1 -1 -1 -1 -1 1/100 -1 1/100 +-100 100 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +-99 100 -0.99 -1 - 98/99 - 99/100 -1 -1 -1 -1 -1 - 99/100 - 99/100 +-98 100 -0.98 -1 - 49/50 - 49/50 -1 -1 -1 -1 -1 - 98/100 - 49/50 +-97 100 -0.97 -1 - 32/33 - 97/100 -1 -1 -1 -1 -1 - 97/100 - 97/100 +-96 100 -0.96 -1 - 24/25 - 24/25 -1 -1 -1 - 15/16 -1 - 96/100 - 24/25 +-95 100 -0.95 -1 - 19/20 - 19/20 -1 -1 -1 - 15/16 -1 - 95/100 - 19/20 +-94 100 -0.94 -1 - 47/50 - 47/50 -1 -1 -1 - 15/16 - 9/10 - 94/100 - 47/50 +-93 100 -0.93 -1 - 40/43 - 93/100 -1 -1 - 7/8 - 15/16 - 9/10 - 93/100 - 93/100 +-92 100 -0.92 -1 - 23/25 - 23/25 -1 -1 - 7/8 - 15/16 - 9/10 - 92/100 - 23/25 +-91 100 -0.91 -1 - 81/89 - 91/100 -1 -1 - 7/8 - 15/16 - 9/10 - 91/100 - 91/100 +-90 100 -0.9 - 8/9 - 9/10 - 9/10 -1 -1 - 7/8 - 14/16 - 9/10 - 90/100 - 9/10 +-89 100 -0.89 - 8/9 - 8/9 - 89/100 -1 -1 - 7/8 - 14/16 - 9/10 - 89/100 - 89/100 +-88 100 -0.88 - 7/8 - 22/25 - 22/25 -1 -1 - 7/8 - 14/16 - 9/10 - 88/100 - 22/25 +-87 100 -0.87 - 7/8 - 67/77 - 87/100 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 87/100 - 87/100 +-86 100 -0.86 - 6/7 - 43/50 - 43/50 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 86/100 - 43/50 +-85 100 -0.85 - 6/7 - 17/20 - 17/20 -1 - 3/4 - 7/8 - 14/16 - 9/10 - 85/100 - 17/20 +-84 100 -0.84 - 5/6 - 21/25 - 21/25 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 84/100 - 21/25 +-83 100 -0.83 - 5/6 - 39/47 - 83/100 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 83/100 - 83/100 +-82 100 -0.82 - 5/6 - 41/50 - 41/50 -1 - 3/4 - 7/8 - 13/16 - 8/10 - 82/100 - 41/50 +-81 100 -0.81 - 4/5 - 64/79 - 81/100 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 81/100 - 81/100 +-80 100 -0.8 - 4/5 - 4/5 - 4/5 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 80/100 - 4/5 +-79 100 -0.79 - 4/5 - 64/81 - 79/100 -1 - 3/4 - 6/8 - 13/16 - 8/10 - 79/100 - 79/100 +-78 100 -0.78 - 7/9 - 39/50 - 39/50 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 78/100 - 39/50 +-77 100 -0.77 - 7/9 - 67/87 - 77/100 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 77/100 - 77/100 +-76 100 -0.76 - 3/4 - 19/25 - 19/25 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 76/100 - 19/25 +-75 100 -0.75 - 3/4 - 3/4 - 3/4 -1 - 3/4 - 6/8 - 12/16 - 8/10 - 75/100 - 3/4 +-74 100 -0.74 - 3/4 - 37/50 - 37/50 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 74/100 - 37/50 +-73 100 -0.73 - 3/4 - 46/63 - 73/100 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 73/100 - 73/100 +-72 100 -0.72 - 5/7 - 18/25 - 18/25 - 1/2 - 3/4 - 6/8 - 12/16 - 7/10 - 72/100 - 18/25 +-71 100 -0.71 - 5/7 - 22/31 - 71/100 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 71/100 - 71/100 +-70 100 -0.7 - 2/3 - 7/10 - 7/10 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 70/100 - 7/10 +-69 100 -0.69 - 2/3 - 20/29 - 69/100 - 1/2 - 3/4 - 6/8 - 11/16 - 7/10 - 69/100 - 69/100 +-68 100 -0.68 - 2/3 - 17/25 - 17/25 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 68/100 - 17/25 +-67 100 -0.67 - 2/3 - 65/97 - 67/100 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 67/100 - 67/100 +-66 100 -0.66 - 2/3 - 33/50 - 33/50 - 1/2 - 3/4 - 5/8 - 11/16 - 7/10 - 66/100 - 33/50 +-65 100 -0.65 - 2/3 - 13/20 - 13/20 - 1/2 - 3/4 - 5/8 - 10/16 - 7/10 - 65/100 - 13/20 +-64 100 -0.64 - 2/3 - 16/25 - 16/25 - 1/2 - 3/4 - 5/8 - 10/16 - 6/10 - 64/100 - 16/25 +-63 100 -0.63 - 5/8 - 46/73 - 63/100 - 1/2 - 3/4 - 5/8 - 10/16 - 6/10 - 63/100 - 63/100 +-62 100 -0.62 - 5/8 - 31/50 - 31/50 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 62/100 - 31/50 +-61 100 -0.61 - 3/5 - 36/59 - 61/100 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 61/100 - 61/100 +-60 100 -0.6 - 3/5 - 3/5 - 3/5 - 1/2 - 2/4 - 5/8 - 10/16 - 6/10 - 60/100 - 3/5 +-59 100 -0.59 - 3/5 - 23/39 - 59/100 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 59/100 - 59/100 +-58 100 -0.58 - 4/7 - 29/50 - 29/50 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 58/100 - 29/50 +-57 100 -0.57 - 4/7 - 53/93 - 57/100 - 1/2 - 2/4 - 5/8 - 9/16 - 6/10 - 57/100 - 57/100 +-56 100 -0.56 - 5/9 - 14/25 - 14/25 - 1/2 - 2/4 - 4/8 - 9/16 - 6/10 - 56/100 - 14/25 +-55 100 -0.55 - 5/9 - 11/20 - 11/20 - 1/2 - 2/4 - 4/8 - 9/16 - 6/10 - 55/100 - 11/20 +-54 100 -0.54 - 1/2 - 27/50 - 27/50 - 1/2 - 2/4 - 4/8 - 9/16 - 5/10 - 54/100 - 27/50 +-53 100 -0.53 - 1/2 - 44/83 - 53/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 53/100 - 53/100 +-52 100 -0.52 - 1/2 - 13/25 - 13/25 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 52/100 - 13/25 +-51 100 -0.51 - 1/2 - 25/49 - 51/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 51/100 - 51/100 +-50 100 -0.5 - 1/2 - 1/2 - 1/2 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 50/100 - 1/2 +-49 100 -0.49 - 1/2 - 24/49 - 49/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 49/100 - 49/100 +-48 100 -0.48 - 1/2 - 12/25 - 12/25 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 48/100 - 12/25 +-47 100 -0.47 - 1/2 - 8/17 - 47/100 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 47/100 - 47/100 +-46 100 -0.46 - 1/2 - 23/50 - 23/50 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 46/100 - 23/50 +-45 100 -0.45 - 4/9 - 9/20 - 9/20 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 45/100 - 9/20 +-44 100 -0.44 - 4/9 - 11/25 - 11/25 - 1/2 - 2/4 - 4/8 - 7/16 - 4/10 - 44/100 - 11/25 +-43 100 -0.43 - 3/7 - 3/7 - 43/100 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 43/100 - 43/100 +-42 100 -0.42 - 3/7 - 21/50 - 21/50 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 42/100 - 21/50 +-41 100 -0.41 - 2/5 - 16/39 - 41/100 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 41/100 - 41/100 +-40 100 -0.4 - 2/5 - 2/5 - 2/5 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 40/100 - 2/5 +-39 100 -0.39 - 2/5 - 16/41 - 39/100 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 39/100 - 39/100 +-38 100 -0.38 - 3/8 - 19/50 - 19/50 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 38/100 - 19/50 +-37 100 -0.37 - 3/8 - 10/27 - 37/100 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 37/100 - 37/100 +-36 100 -0.36 - 1/3 - 9/25 - 9/25 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 36/100 - 9/25 +-35 100 -0.35 - 1/3 - 7/20 - 7/20 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 35/100 - 7/20 +-34 100 -0.34 - 1/3 - 17/50 - 17/50 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 34/100 - 17/50 +-33 100 -0.33 - 1/3 - 1/3 - 33/100 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 33/100 - 33/100 +-32 100 -0.32 - 1/3 - 8/25 - 8/25 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 32/100 - 8/25 +-31 100 -0.31 - 1/3 - 22/71 - 31/100 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 31/100 - 31/100 +-30 100 -0.3 - 2/7 - 3/10 - 3/10 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 30/100 - 3/10 +-29 100 -0.29 - 2/7 - 20/69 - 29/100 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 29/100 - 29/100 +-28 100 -0.28 - 2/7 - 7/25 - 7/25 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 28/100 - 7/25 +-27 100 -0.27 - 1/4 - 10/37 - 27/100 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 27/100 - 27/100 +-26 100 -0.26 - 1/4 - 13/50 - 13/50 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 26/100 - 13/50 +-25 100 -0.25 - 1/4 - 1/4 - 1/4 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 25/100 - 1/4 +-24 100 -0.24 - 1/4 - 6/25 - 6/25 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 24/100 - 6/25 +-23 100 -0.23 - 2/9 - 3/13 - 23/100 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 23/100 - 23/100 +-22 100 -0.22 - 2/9 - 11/50 - 11/50 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 22/100 - 11/50 +-21 100 -0.21 - 1/5 - 17/81 - 21/100 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 21/100 - 21/100 +-20 100 -0.2 - 1/5 - 1/5 - 1/5 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 20/100 - 1/5 +-19 100 -0.19 - 1/5 - 15/79 - 19/100 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 19/100 - 19/100 +-18 100 -0.18 - 1/6 - 9/50 - 9/50 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 18/100 - 9/50 +-17 100 -0.17 - 1/6 - 8/47 - 17/100 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 17/100 - 17/100 +-16 100 -0.16 - 1/6 - 4/25 - 4/25 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 16/100 - 4/25 +-15 100 -0.15 - 1/7 - 3/20 - 3/20 -0 - 1/4 - 1/8 - 2/16 - 2/10 - 15/100 - 3/20 +-14 100 -0.14 - 1/7 - 7/50 - 7/50 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 14/100 - 7/50 +-13 100 -0.13 - 1/8 - 3/23 - 13/100 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 13/100 - 13/100 +-12 100 -0.12 - 1/8 - 3/25 - 3/25 -0 -0 - 1/8 - 2/16 - 1/10 - 12/100 - 3/25 +-11 100 -0.11 - 1/9 - 10/91 - 11/100 -0 -0 - 1/8 - 2/16 - 1/10 - 11/100 - 11/100 +-10 100 -0.1 -0 - 1/10 - 1/10 -0 -0 - 1/8 - 2/16 - 1/10 - 10/100 - 1/10 +-9 100 -0.09 -0 - 1/11 - 9/100 -0 -0 - 1/8 - 1/16 - 1/10 - 9/100 - 9/100 +-8 100 -0.08 -0 - 2/25 - 2/25 -0 -0 - 1/8 - 1/16 - 1/10 - 8/100 - 2/25 +-7 100 -0.07 -0 - 4/57 - 7/100 -0 -0 - 1/8 - 1/16 - 1/10 - 7/100 - 7/100 +-6 100 -0.06 -0 - 3/50 - 3/50 -0 -0 -0 - 1/16 - 1/10 - 6/100 - 3/50 +-5 100 -0.05 -0 - 1/20 - 1/20 -0 -0 -0 - 1/16 - 1/10 - 5/100 - 1/20 +-4 100 -0.04 -0 - 1/25 - 1/25 -0 -0 -0 - 1/16 -0 - 4/100 - 1/25 +-3 100 -0.03 -0 - 2/67 - 3/100 -0 -0 -0 -0 -0 - 3/100 - 3/100 +-2 100 -0.02 -0 - 1/50 - 1/50 -0 -0 -0 -0 -0 - 2/100 - 1/50 +-1 100 -0.01 -0 -0 - 1/100 -0 -0 -0 -0 -0 - 1/100 - 1/100 +0 100 0 0 0 0 0 0 0 0 0 0 0 +1 100 0.01 0 0 1/100 0 0 0 0 0 1/100 1/100 +2 100 0.02 0 1/50 1/50 0 0 0 0 0 2/100 1/50 +3 100 0.03 0 2/67 3/100 0 0 0 0 0 3/100 3/100 +4 100 0.04 0 1/25 1/25 0 0 0 1/16 0 4/100 1/25 +5 100 0.05 0 1/20 1/20 0 0 0 1/16 1/10 5/100 1/20 +6 100 0.06 0 3/50 3/50 0 0 0 1/16 1/10 6/100 3/50 +7 100 0.07 0 4/57 7/100 0 0 1/8 1/16 1/10 7/100 7/100 +8 100 0.08 0 2/25 2/25 0 0 1/8 1/16 1/10 8/100 2/25 +9 100 0.09 0 1/11 9/100 0 0 1/8 1/16 1/10 9/100 9/100 +10 100 0.1 0 1/10 1/10 0 0 1/8 2/16 1/10 10/100 1/10 +11 100 0.11 1/9 10/91 11/100 0 0 1/8 2/16 1/10 11/100 11/100 +12 100 0.12 1/8 3/25 3/25 0 0 1/8 2/16 1/10 12/100 3/25 +13 100 0.13 1/8 3/23 13/100 0 1/4 1/8 2/16 1/10 13/100 13/100 +14 100 0.14 1/7 7/50 7/50 0 1/4 1/8 2/16 1/10 14/100 7/50 +15 100 0.15 1/7 3/20 3/20 0 1/4 1/8 2/16 2/10 15/100 3/20 +16 100 0.16 1/6 4/25 4/25 0 1/4 1/8 3/16 2/10 16/100 4/25 +17 100 0.17 1/6 8/47 17/100 0 1/4 1/8 3/16 2/10 17/100 17/100 +18 100 0.18 1/6 9/50 9/50 0 1/4 1/8 3/16 2/10 18/100 9/50 +19 100 0.19 1/5 15/79 19/100 0 1/4 2/8 3/16 2/10 19/100 19/100 +20 100 0.2 1/5 1/5 1/5 0 1/4 2/8 3/16 2/10 20/100 1/5 +21 100 0.21 1/5 17/81 21/100 0 1/4 2/8 3/16 2/10 21/100 21/100 +22 100 0.22 2/9 11/50 11/50 0 1/4 2/8 4/16 2/10 22/100 11/50 +23 100 0.23 2/9 3/13 23/100 0 1/4 2/8 4/16 2/10 23/100 23/100 +24 100 0.24 1/4 6/25 6/25 0 1/4 2/8 4/16 2/10 24/100 6/25 +25 100 0.25 1/4 1/4 1/4 1/2 1/4 2/8 4/16 3/10 25/100 1/4 +26 100 0.26 1/4 13/50 13/50 1/2 1/4 2/8 4/16 3/10 26/100 13/50 +27 100 0.27 1/4 10/37 27/100 1/2 1/4 2/8 4/16 3/10 27/100 27/100 +28 100 0.28 2/7 7/25 7/25 1/2 1/4 2/8 4/16 3/10 28/100 7/25 +29 100 0.29 2/7 20/69 29/100 1/2 1/4 2/8 5/16 3/10 29/100 29/100 +30 100 0.3 2/7 3/10 3/10 1/2 1/4 2/8 5/16 3/10 30/100 3/10 +31 100 0.31 1/3 22/71 31/100 1/2 1/4 2/8 5/16 3/10 31/100 31/100 +32 100 0.32 1/3 8/25 8/25 1/2 1/4 3/8 5/16 3/10 32/100 8/25 +33 100 0.33 1/3 1/3 33/100 1/2 1/4 3/8 5/16 3/10 33/100 33/100 +34 100 0.34 1/3 17/50 17/50 1/2 1/4 3/8 5/16 3/10 34/100 17/50 +35 100 0.35 1/3 7/20 7/20 1/2 1/4 3/8 6/16 4/10 35/100 7/20 +36 100 0.36 1/3 9/25 9/25 1/2 1/4 3/8 6/16 4/10 36/100 9/25 +37 100 0.37 3/8 10/27 37/100 1/2 1/4 3/8 6/16 4/10 37/100 37/100 +38 100 0.38 3/8 19/50 19/50 1/2 2/4 3/8 6/16 4/10 38/100 19/50 +39 100 0.39 2/5 16/41 39/100 1/2 2/4 3/8 6/16 4/10 39/100 39/100 +40 100 0.4 2/5 2/5 2/5 1/2 2/4 3/8 6/16 4/10 40/100 2/5 +41 100 0.41 2/5 16/39 41/100 1/2 2/4 3/8 7/16 4/10 41/100 41/100 +42 100 0.42 3/7 21/50 21/50 1/2 2/4 3/8 7/16 4/10 42/100 21/50 +43 100 0.43 3/7 3/7 43/100 1/2 2/4 3/8 7/16 4/10 43/100 43/100 +44 100 0.44 4/9 11/25 11/25 1/2 2/4 4/8 7/16 4/10 44/100 11/25 +45 100 0.45 4/9 9/20 9/20 1/2 2/4 4/8 7/16 5/10 45/100 9/20 +46 100 0.46 1/2 23/50 23/50 1/2 2/4 4/8 7/16 5/10 46/100 23/50 +47 100 0.47 1/2 8/17 47/100 1/2 2/4 4/8 8/16 5/10 47/100 47/100 +48 100 0.48 1/2 12/25 12/25 1/2 2/4 4/8 8/16 5/10 48/100 12/25 +49 100 0.49 1/2 24/49 49/100 1/2 2/4 4/8 8/16 5/10 49/100 49/100 +50 100 0.5 1/2 1/2 1/2 1/2 2/4 4/8 8/16 5/10 50/100 1/2 +51 100 0.51 1/2 25/49 51/100 1/2 2/4 4/8 8/16 5/10 51/100 51/100 +52 100 0.52 1/2 13/25 13/25 1/2 2/4 4/8 8/16 5/10 52/100 13/25 +53 100 0.53 1/2 44/83 53/100 1/2 2/4 4/8 8/16 5/10 53/100 53/100 +54 100 0.54 1/2 27/50 27/50 1/2 2/4 4/8 9/16 5/10 54/100 27/50 +55 100 0.55 5/9 11/20 11/20 1/2 2/4 4/8 9/16 6/10 55/100 11/20 +56 100 0.56 5/9 14/25 14/25 1/2 2/4 4/8 9/16 6/10 56/100 14/25 +57 100 0.57 4/7 53/93 57/100 1/2 2/4 5/8 9/16 6/10 57/100 57/100 +58 100 0.58 4/7 29/50 29/50 1/2 2/4 5/8 9/16 6/10 58/100 29/50 +59 100 0.59 3/5 23/39 59/100 1/2 2/4 5/8 9/16 6/10 59/100 59/100 +60 100 0.6 3/5 3/5 3/5 1/2 2/4 5/8 10/16 6/10 60/100 3/5 +61 100 0.61 3/5 36/59 61/100 1/2 2/4 5/8 10/16 6/10 61/100 61/100 +62 100 0.62 5/8 31/50 31/50 1/2 2/4 5/8 10/16 6/10 62/100 31/50 +63 100 0.63 5/8 46/73 63/100 1/2 3/4 5/8 10/16 6/10 63/100 63/100 +64 100 0.64 2/3 16/25 16/25 1/2 3/4 5/8 10/16 6/10 64/100 16/25 +65 100 0.65 2/3 13/20 13/20 1/2 3/4 5/8 10/16 7/10 65/100 13/20 +66 100 0.66 2/3 33/50 33/50 1/2 3/4 5/8 11/16 7/10 66/100 33/50 +67 100 0.67 2/3 65/97 67/100 1/2 3/4 5/8 11/16 7/10 67/100 67/100 +68 100 0.68 2/3 17/25 17/25 1/2 3/4 5/8 11/16 7/10 68/100 17/25 +69 100 0.69 2/3 20/29 69/100 1/2 3/4 6/8 11/16 7/10 69/100 69/100 +70 100 0.7 2/3 7/10 7/10 1/2 3/4 6/8 11/16 7/10 70/100 7/10 +71 100 0.71 5/7 22/31 71/100 1/2 3/4 6/8 11/16 7/10 71/100 71/100 +72 100 0.72 5/7 18/25 18/25 1/2 3/4 6/8 12/16 7/10 72/100 18/25 +73 100 0.73 3/4 46/63 73/100 1/2 3/4 6/8 12/16 7/10 73/100 73/100 +74 100 0.74 3/4 37/50 37/50 1/2 3/4 6/8 12/16 7/10 74/100 37/50 +75 100 0.75 3/4 3/4 3/4 1 3/4 6/8 12/16 8/10 75/100 3/4 +76 100 0.76 3/4 19/25 19/25 1 3/4 6/8 12/16 8/10 76/100 19/25 +77 100 0.77 7/9 67/87 77/100 1 3/4 6/8 12/16 8/10 77/100 77/100 +78 100 0.78 7/9 39/50 39/50 1 3/4 6/8 12/16 8/10 78/100 39/50 +79 100 0.79 4/5 64/81 79/100 1 3/4 6/8 13/16 8/10 79/100 79/100 +80 100 0.8 4/5 4/5 4/5 1 3/4 6/8 13/16 8/10 80/100 4/5 +81 100 0.81 4/5 64/79 81/100 1 3/4 6/8 13/16 8/10 81/100 81/100 +82 100 0.82 5/6 41/50 41/50 1 3/4 7/8 13/16 8/10 82/100 41/50 +83 100 0.83 5/6 39/47 83/100 1 3/4 7/8 13/16 8/10 83/100 83/100 +84 100 0.84 5/6 21/25 21/25 1 3/4 7/8 13/16 8/10 84/100 21/25 +85 100 0.85 6/7 17/20 17/20 1 3/4 7/8 14/16 9/10 85/100 17/20 +86 100 0.86 6/7 43/50 43/50 1 3/4 7/8 14/16 9/10 86/100 43/50 +87 100 0.87 7/8 67/77 87/100 1 3/4 7/8 14/16 9/10 87/100 87/100 +88 100 0.88 7/8 22/25 22/25 1 1 7/8 14/16 9/10 88/100 22/25 +89 100 0.89 8/9 8/9 89/100 1 1 7/8 14/16 9/10 89/100 89/100 +90 100 0.9 8/9 9/10 9/10 1 1 7/8 14/16 9/10 90/100 9/10 +91 100 0.91 1 81/89 91/100 1 1 7/8 15/16 9/10 91/100 91/100 +92 100 0.92 1 23/25 23/25 1 1 7/8 15/16 9/10 92/100 23/25 +93 100 0.93 1 40/43 93/100 1 1 7/8 15/16 9/10 93/100 93/100 +94 100 0.94 1 47/50 47/50 1 1 1 15/16 9/10 94/100 47/50 +95 100 0.95 1 19/20 19/20 1 1 1 15/16 1 95/100 19/20 +96 100 0.96 1 24/25 24/25 1 1 1 15/16 1 96/100 24/25 +97 100 0.97 1 32/33 97/100 1 1 1 1 1 97/100 97/100 +98 100 0.98 1 49/50 49/50 1 1 1 1 1 98/100 49/50 +99 100 0.99 1 98/99 99/100 1 1 1 1 1 99/100 99/100 +100 100 1 1 1 1 1 1 1 1 1 1 1 +101 100 1.01 1 1 1/99 1 1/100 1 1 1 1 1 1 1/100 1 1/100 +102 100 1.02 1 1 1/50 1 1/50 1 1 1 1 1 1 2/100 1 1/50 +103 100 1.03 1 1 1/33 1 3/100 1 1 1 1 1 1 3/100 1 3/100 +104 100 1.04 1 1 1/25 1 1/25 1 1 1 1 1/16 1 1 4/100 1 1/25 +105 100 1.05 1 1 1/20 1 1/20 1 1 1 1 1/16 1 1/10 1 5/100 1 1/20 +106 100 1.06 1 1 3/50 1 3/50 1 1 1 1 1/16 1 1/10 1 6/100 1 3/50 +107 100 1.07 1 1 4/57 1 7/100 1 1 1 1/8 1 1/16 1 1/10 1 7/100 1 7/100 +108 100 1.08 1 1 2/25 1 2/25 1 1 1 1/8 1 1/16 1 1/10 1 8/100 1 2/25 +109 100 1.09 1 1 1/11 1 9/100 1 1 1 1/8 1 1/16 1 1/10 1 9/100 1 9/100 +110 100 1.1 1 1/9 1 1/10 1 1/10 1 1 1 1/8 1 2/16 1 1/10 1 10/100 1 1/10 +111 100 1.11 1 1/9 1 1/9 1 11/100 1 1 1 1/8 1 2/16 1 1/10 1 11/100 1 11/100 +112 100 1.12 1 1/8 1 3/25 1 3/25 1 1 1 1/8 1 2/16 1 1/10 1 12/100 1 3/25 +113 100 1.13 1 1/8 1 10/77 1 13/100 1 1 1/4 1 1/8 1 2/16 1 1/10 1 13/100 1 13/100 +114 100 1.14 1 1/7 1 7/50 1 7/50 1 1 1/4 1 1/8 1 2/16 1 1/10 1 14/100 1 7/50 +115 100 1.15 1 1/7 1 3/20 1 3/20 1 1 1/4 1 1/8 1 2/16 1 1/10 1 15/100 1 3/20 +116 100 1.16 1 1/6 1 4/25 1 4/25 1 1 1/4 1 1/8 1 3/16 1 2/10 1 16/100 1 4/25 +117 100 1.17 1 1/6 1 9/53 1 17/100 1 1 1/4 1 1/8 1 3/16 1 2/10 1 17/100 1 17/100 +118 100 1.18 1 1/6 1 9/50 1 9/50 1 1 1/4 1 1/8 1 3/16 1 2/10 1 18/100 1 9/50 +119 100 1.19 1 1/5 1 15/79 1 19/100 1 1 1/4 1 2/8 1 3/16 1 2/10 1 19/100 1 19/100 +120 100 1.2 1 1/5 1 1/5 1 1/5 1 1 1/4 1 2/8 1 3/16 1 2/10 1 20/100 1 1/5 +121 100 1.21 1 1/5 1 17/81 1 21/100 1 1 1/4 1 2/8 1 3/16 1 2/10 1 21/100 1 21/100 +122 100 1.22 1 2/9 1 11/50 1 11/50 1 1 1/4 1 2/8 1 4/16 1 2/10 1 22/100 1 11/50 +123 100 1.23 1 2/9 1 20/87 1 23/100 1 1 1/4 1 2/8 1 4/16 1 2/10 1 23/100 1 23/100 +124 100 1.24 1 1/4 1 6/25 1 6/25 1 1 1/4 1 2/8 1 4/16 1 2/10 1 24/100 1 6/25 +125 100 1.25 1 1/4 1 1/4 1 1/4 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 25/100 1 1/4 +126 100 1.26 1 1/4 1 13/50 1 13/50 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 26/100 1 13/50 +127 100 1.27 1 1/4 1 10/37 1 27/100 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 27/100 1 27/100 +-103 201 -0.512437811 - 1/2 - 41/80 - 103/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 51/100 - 103/201 +-100 201 -0.497512438 - 1/2 - 1/2 - 100/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 50/100 - 100/201 +-97 201 -0.482587065 - 1/2 - 14/29 - 97/201 - 1/2 - 2/4 - 4/8 - 8/16 - 5/10 - 48/100 - 97/201 +-94 201 -0.467661692 - 1/2 - 29/62 - 94/201 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 47/100 - 94/201 +-91 201 -0.452736318 - 4/9 - 24/53 - 91/201 - 1/2 - 2/4 - 4/8 - 7/16 - 5/10 - 45/100 - 91/201 +-88 201 -0.437810945 - 4/9 - 7/16 - 88/201 - 1/2 - 2/4 - 4/8 - 7/16 - 4/10 - 44/100 - 88/201 +-85 201 -0.422885572 - 3/7 - 11/26 - 85/201 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 42/100 - 85/201 +-82 201 -0.407960199 - 2/5 - 31/76 - 82/201 - 1/2 - 2/4 - 3/8 - 7/16 - 4/10 - 41/100 - 82/201 +-79 201 -0.393034826 - 2/5 - 11/28 - 79/201 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 39/100 - 79/201 +-76 201 -0.378109453 - 3/8 - 31/82 - 76/201 - 1/2 - 2/4 - 3/8 - 6/16 - 4/10 - 38/100 - 76/201 +-73 201 -0.36318408 - 1/3 - 4/11 - 73/201 - 1/2 - 1/4 - 3/8 - 6/16 - 4/10 - 36/100 - 73/201 +-70 201 -0.348258706 - 1/3 - 31/89 - 70/201 - 1/2 - 1/4 - 3/8 - 6/16 - 3/10 - 35/100 - 70/201 +-67 201 -0.333333333 - 1/3 - 1/3 - 1/3 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 33/100 - 1/3 +-64 201 -0.31840796 - 1/3 - 7/22 - 64/201 - 1/2 - 1/4 - 3/8 - 5/16 - 3/10 - 32/100 - 64/201 +-61 201 -0.303482587 - 1/3 - 17/56 - 61/201 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 30/100 - 61/201 +-58 201 -0.288557214 - 2/7 - 15/52 - 58/201 - 1/2 - 1/4 - 2/8 - 5/16 - 3/10 - 29/100 - 58/201 +-55 201 -0.273631841 - 2/7 - 26/95 - 55/201 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 27/100 - 55/201 +-52 201 -0.258706468 - 1/4 - 15/58 - 52/201 - 1/2 - 1/4 - 2/8 - 4/16 - 3/10 - 26/100 - 52/201 +-49 201 -0.243781095 - 1/4 - 10/41 - 49/201 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 24/100 - 49/201 +-46 201 -0.228855721 - 2/9 - 19/83 - 46/201 -0 - 1/4 - 2/8 - 4/16 - 2/10 - 23/100 - 46/201 +-43 201 -0.213930348 - 1/5 - 3/14 - 43/201 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 21/100 - 43/201 +-40 201 -0.199004975 - 1/5 - 1/5 - 40/201 -0 - 1/4 - 2/8 - 3/16 - 2/10 - 20/100 - 40/201 +-37 201 -0.184079602 - 1/5 - 7/38 - 37/201 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 18/100 - 37/201 +-34 201 -0.169154229 - 1/6 - 11/65 - 34/201 -0 - 1/4 - 1/8 - 3/16 - 2/10 - 17/100 - 34/201 +-31 201 -0.154228856 - 1/6 - 2/13 - 31/201 -0 - 1/4 - 1/8 - 2/16 - 2/10 - 15/100 - 31/201 +-28 201 -0.139303483 - 1/7 - 11/79 - 28/201 -0 - 1/4 - 1/8 - 2/16 - 1/10 - 14/100 - 28/201 +-25 201 -0.124378109 - 1/8 - 1/8 - 25/201 -0 -0 - 1/8 - 2/16 - 1/10 - 12/100 - 25/201 +-22 201 -0.109452736 - 1/9 - 7/64 - 22/201 -0 -0 - 1/8 - 2/16 - 1/10 - 11/100 - 22/201 +-19 201 -0.094527363 -0 - 7/74 - 19/201 -0 -0 - 1/8 - 2/16 - 1/10 - 9/100 - 19/201 +-16 201 -0.07960199 -0 - 7/88 - 16/201 -0 -0 - 1/8 - 1/16 - 1/10 - 8/100 - 16/201 +-13 201 -0.064676617 -0 - 2/31 - 13/201 -0 -0 - 1/8 - 1/16 - 1/10 - 6/100 - 13/201 +-10 201 -0.049751244 -0 - 1/20 - 10/201 -0 -0 -0 - 1/16 -0 - 5/100 - 10/201 +-7 201 -0.034825871 -0 - 3/86 - 7/201 -0 -0 -0 - 1/16 -0 - 3/100 - 7/201 +-4 201 -0.019900498 -0 - 1/50 - 4/201 -0 -0 -0 -0 -0 - 2/100 - 4/201 +-1 201 -0.004975124 -0 -0 - 1/201 -0 -0 -0 -0 -0 -0 - 1/201 +2 201 0.009950249 0 0 2/201 0 0 0 0 0 1/100 2/201 +5 201 0.024875622 0 1/40 5/201 0 0 0 0 0 2/100 5/201 +8 201 0.039800995 0 1/25 8/201 0 0 0 1/16 0 4/100 8/201 +11 201 0.054726368 0 4/73 11/201 0 0 0 1/16 1/10 5/100 11/201 +14 201 0.069651741 0 3/43 14/201 0 0 1/8 1/16 1/10 7/100 14/201 +17 201 0.084577114 0 6/71 17/201 0 0 1/8 1/16 1/10 8/100 17/201 +20 201 0.099502488 0 1/10 20/201 0 0 1/8 2/16 1/10 10/100 20/201 +23 201 0.114427861 1/9 4/35 23/201 0 0 1/8 2/16 1/10 11/100 23/201 +26 201 0.129353234 1/8 11/85 26/201 0 1/4 1/8 2/16 1/10 13/100 26/201 +29 201 0.144278607 1/7 14/97 29/201 0 1/4 1/8 2/16 1/10 14/100 29/201 +32 201 0.15920398 1/6 7/44 32/201 0 1/4 1/8 3/16 2/10 16/100 32/201 +35 201 0.174129353 1/6 4/23 35/201 0 1/4 1/8 3/16 2/10 17/100 35/201 +38 201 0.189054726 1/5 7/37 38/201 0 1/4 2/8 3/16 2/10 19/100 38/201 +41 201 0.2039801 1/5 10/49 41/201 0 1/4 2/8 3/16 2/10 20/100 41/201 +44 201 0.218905473 2/9 7/32 44/201 0 1/4 2/8 4/16 2/10 22/100 44/201 +47 201 0.233830846 1/4 18/77 47/201 0 1/4 2/8 4/16 2/10 23/100 47/201 +50 201 0.248756219 1/4 1/4 50/201 0 1/4 2/8 4/16 2/10 25/100 50/201 +53 201 0.263681592 1/4 24/91 53/201 1/2 1/4 2/8 4/16 3/10 26/100 53/201 +56 201 0.278606965 2/7 17/61 56/201 1/2 1/4 2/8 4/16 3/10 28/100 56/201 +59 201 0.293532338 2/7 27/92 59/201 1/2 1/4 2/8 5/16 3/10 29/100 59/201 +62 201 0.308457711 1/3 29/94 62/201 1/2 1/4 2/8 5/16 3/10 31/100 62/201 +65 201 0.323383085 1/3 11/34 65/201 1/2 1/4 3/8 5/16 3/10 32/100 65/201 +68 201 0.338308458 1/3 23/68 68/201 1/2 1/4 3/8 5/16 3/10 34/100 68/201 +71 201 0.353233831 1/3 6/17 71/201 1/2 1/4 3/8 6/16 4/10 35/100 71/201 +74 201 0.368159204 3/8 7/19 74/201 1/2 1/4 3/8 6/16 4/10 37/100 74/201 +77 201 0.383084577 3/8 18/47 77/201 1/2 2/4 3/8 6/16 4/10 38/100 77/201 +80 201 0.39800995 2/5 39/98 80/201 1/2 2/4 3/8 6/16 4/10 40/100 80/201 +83 201 0.412935323 2/5 19/46 83/201 1/2 2/4 3/8 7/16 4/10 41/100 83/201 +86 201 0.427860697 3/7 3/7 86/201 1/2 2/4 3/8 7/16 4/10 43/100 86/201 +89 201 0.44278607 4/9 31/70 89/201 1/2 2/4 4/8 7/16 4/10 44/100 89/201 +92 201 0.457711443 1/2 27/59 92/201 1/2 2/4 4/8 7/16 5/10 46/100 92/201 +95 201 0.472636816 1/2 26/55 95/201 1/2 2/4 4/8 8/16 5/10 47/100 95/201 +98 201 0.487562189 1/2 39/80 98/201 1/2 2/4 4/8 8/16 5/10 49/100 98/201 +101 201 0.502487562 1/2 1/2 101/201 1/2 2/4 4/8 8/16 5/10 50/100 101/201 +104 201 0.517412935 1/2 15/29 104/201 1/2 2/4 4/8 8/16 5/10 52/100 104/201 +107 201 0.532338308 1/2 33/62 107/201 1/2 2/4 4/8 9/16 5/10 53/100 107/201 +110 201 0.547263682 5/9 29/53 110/201 1/2 2/4 4/8 9/16 5/10 55/100 110/201 +113 201 0.562189055 5/9 9/16 113/201 1/2 2/4 4/8 9/16 6/10 56/100 113/201 +116 201 0.577114428 4/7 15/26 116/201 1/2 2/4 5/8 9/16 6/10 58/100 116/201 +119 201 0.592039801 3/5 45/76 119/201 1/2 2/4 5/8 9/16 6/10 59/100 119/201 +122 201 0.606965174 3/5 17/28 122/201 1/2 2/4 5/8 10/16 6/10 61/100 122/201 +125 201 0.621890547 5/8 51/82 125/201 1/2 2/4 5/8 10/16 6/10 62/100 125/201 +128 201 0.63681592 2/3 7/11 128/201 1/2 3/4 5/8 10/16 6/10 64/100 128/201 +131 201 0.651741294 2/3 58/89 131/201 1/2 3/4 5/8 10/16 7/10 65/100 131/201 +134 201 0.666666667 2/3 2/3 2/3 1/2 3/4 5/8 11/16 7/10 67/100 2/3 +137 201 0.68159204 2/3 15/22 137/201 1/2 3/4 5/8 11/16 7/10 68/100 137/201 +140 201 0.696517413 2/3 39/56 140/201 1/2 3/4 6/8 11/16 7/10 70/100 140/201 +143 201 0.711442786 5/7 37/52 143/201 1/2 3/4 6/8 11/16 7/10 71/100 143/201 +146 201 0.726368159 5/7 69/95 146/201 1/2 3/4 6/8 12/16 7/10 73/100 146/201 +149 201 0.741293532 3/4 43/58 149/201 1/2 3/4 6/8 12/16 7/10 74/100 149/201 +152 201 0.756218905 3/4 31/41 152/201 1 3/4 6/8 12/16 8/10 76/100 152/201 +155 201 0.771144279 7/9 64/83 155/201 1 3/4 6/8 12/16 8/10 77/100 155/201 +158 201 0.786069652 4/5 11/14 158/201 1 3/4 6/8 13/16 8/10 79/100 158/201 +161 201 0.800995025 4/5 4/5 161/201 1 3/4 6/8 13/16 8/10 80/100 161/201 +164 201 0.815920398 4/5 31/38 164/201 1 3/4 7/8 13/16 8/10 82/100 164/201 +167 201 0.830845771 5/6 54/65 167/201 1 3/4 7/8 13/16 8/10 83/100 167/201 +170 201 0.845771144 5/6 11/13 170/201 1 3/4 7/8 14/16 8/10 85/100 170/201 +173 201 0.860696517 6/7 68/79 173/201 1 3/4 7/8 14/16 9/10 86/100 173/201 +176 201 0.875621891 7/8 7/8 176/201 1 1 7/8 14/16 9/10 88/100 176/201 +179 201 0.890547264 8/9 57/64 179/201 1 1 7/8 14/16 9/10 89/100 179/201 +182 201 0.905472637 1 67/74 182/201 1 1 7/8 14/16 9/10 91/100 182/201 +185 201 0.92039801 1 81/88 185/201 1 1 7/8 15/16 9/10 92/100 185/201 +188 201 0.935323383 1 29/31 188/201 1 1 7/8 15/16 9/10 94/100 188/201 +191 201 0.950248756 1 19/20 191/201 1 1 1 15/16 1 95/100 191/201 +194 201 0.965174129 1 83/86 194/201 1 1 1 15/16 1 97/100 194/201 +197 201 0.980099502 1 49/50 197/201 1 1 1 1 1 98/100 197/201 +200 201 0.995024876 1 1 200/201 1 1 1 1 1 1 200/201 +203 201 1.009950249 1 1 1 2/201 1 1 1 1 1 1 1/100 1 2/201 +206 201 1.024875622 1 1 1/40 1 5/201 1 1 1 1 1 1 2/100 1 5/201 +209 201 1.039800995 1 1 1/25 1 8/201 1 1 1 1 1/16 1 1 4/100 1 8/201 +212 201 1.054726368 1 1 4/73 1 11/201 1 1 1 1 1/16 1 1/10 1 5/100 1 11/201 +215 201 1.069651741 1 1 3/43 1 14/201 1 1 1 1/8 1 1/16 1 1/10 1 7/100 1 14/201 +218 201 1.084577114 1 1 6/71 1 17/201 1 1 1 1/8 1 1/16 1 1/10 1 8/100 1 17/201 +221 201 1.099502488 1 1 1/10 1 20/201 1 1 1 1/8 1 2/16 1 1/10 1 10/100 1 20/201 +224 201 1.114427861 1 1/9 1 4/35 1 23/201 1 1 1 1/8 1 2/16 1 1/10 1 11/100 1 23/201 +227 201 1.129353234 1 1/8 1 11/85 1 26/201 1 1 1/4 1 1/8 1 2/16 1 1/10 1 13/100 1 26/201 +230 201 1.144278607 1 1/7 1 14/97 1 29/201 1 1 1/4 1 1/8 1 2/16 1 1/10 1 14/100 1 29/201 +233 201 1.15920398 1 1/6 1 7/44 1 32/201 1 1 1/4 1 1/8 1 3/16 1 2/10 1 16/100 1 32/201 +236 201 1.174129353 1 1/6 1 4/23 1 35/201 1 1 1/4 1 1/8 1 3/16 1 2/10 1 17/100 1 35/201 +239 201 1.189054726 1 1/5 1 7/37 1 38/201 1 1 1/4 1 2/8 1 3/16 1 2/10 1 19/100 1 38/201 +242 201 1.2039801 1 1/5 1 10/49 1 41/201 1 1 1/4 1 2/8 1 3/16 1 2/10 1 20/100 1 41/201 +245 201 1.218905473 1 2/9 1 7/32 1 44/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 22/100 1 44/201 +248 201 1.233830846 1 1/4 1 18/77 1 47/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 23/100 1 47/201 +251 201 1.248756219 1 1/4 1 1/4 1 50/201 1 1 1/4 1 2/8 1 4/16 1 2/10 1 25/100 1 50/201 +254 201 1.263681592 1 1/4 1 24/91 1 53/201 1 1/2 1 1/4 1 2/8 1 4/16 1 3/10 1 26/100 1 53/201 + + + + + + + + + + + + + + + + + + + diff --git a/test-data/spreadsheet/SampleSS.xml b/test-data/spreadsheet/SampleSS.xml index 45cd58cec1..1c6de1466a 100644 --- a/test-data/spreadsheet/SampleSS.xml +++ b/test-data/spreadsheet/SampleSS.xml @@ -1,142 +1,142 @@ - - - - - Sample Spreadsheet - Spreadsheet for testing - Nick Burch - Testing Sample Formulas - This is a sample spreadsheet, for use when testing things - Nick Burch - 2008-01-04T11:51:36Z - 2008-01-04T11:56:04Z - 14.00 - - - - - - 5580 - 11295 - 360 - 60 - 1 - False - False - - - - - - - - - - Test spreadsheet - - - 2nd row - 2nd row 2nd column - - - This one is red - -
    - - -
    -