summaryrefslogtreecommitdiffstats
path: root/filter/qa
diff options
context:
space:
mode:
Diffstat (limited to 'filter/qa')
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/Helper.java431
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/TypeDetection.java546
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/TypeDetection.props31
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/files.csv117
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/makefile.mk109
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/preselectedFilter.csv6
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/preselectedType.csv6
-rw-r--r--filter/qa/complex/filter/detection/typeDetection/serviceName.csv6
-rw-r--r--filter/qa/complex/filter/misc/FinalizedMandatoryTest.java304
-rw-r--r--filter/qa/complex/filter/misc/TypeDetection6FileFormat.java127
-rw-r--r--filter/qa/complex/filter/misc/TypeDetection6FileFormat.xcu42
-rw-r--r--filter/qa/cppunit/data/xslt/copy.xslt9
-rw-r--r--filter/qa/cppunit/msfilter-test.cxx76
-rw-r--r--filter/qa/cppunit/priority-test.cxx85
-rw-r--r--filter/qa/cppunit/xslt-test.cxx207
-rw-r--r--filter/qa/data/picture.pdfbin0 -> 13829 bytes
-rw-r--r--filter/qa/pdf.cxx347
-rw-r--r--filter/qa/unit/data/TransparentText.odgbin0 -> 9354 bytes
-rw-r--r--filter/qa/unit/data/attributeRedefinedTest.odpbin0 -> 12610 bytes
-rw-r--r--filter/qa/unit/data/calc.otsbin0 -> 9564 bytes
-rw-r--r--filter/qa/unit/data/custom-bullet.fodp36
-rw-r--r--filter/qa/unit/data/empty.doc0
-rw-r--r--filter/qa/unit/data/empty.odp0
-rw-r--r--filter/qa/unit/data/empty.ods0
-rw-r--r--filter/qa/unit/data/empty.odt0
-rw-r--r--filter/qa/unit/data/empty.pptx0
-rw-r--r--filter/qa/unit/data/filter-dialogs-test.txt49
-rw-r--r--filter/qa/unit/data/impress.otpbin0 -> 15382 bytes
-rw-r--r--filter/qa/unit/data/preserve-jpg.odtbin0 -> 10052 bytes
-rw-r--r--filter/qa/unit/data/semi-transparent-fill.odgbin0 -> 10597 bytes
-rw-r--r--filter/qa/unit/data/semi-transparent-line.odgbin0 -> 8874 bytes
-rw-r--r--filter/qa/unit/data/shape-nographic.odpbin0 -> 14769 bytes
-rw-r--r--filter/qa/unit/data/tdf114428.xhtml9
-rw-r--r--filter/qa/unit/data/writer.ottbin0 -> 10017 bytes
-rw-r--r--filter/qa/unit/filter-dialogs-test.cxx61
-rw-r--r--filter/qa/unit/svg.cxx380
-rw-r--r--filter/qa/unit/textfilterdetect.cxx199
37 files changed, 3183 insertions, 0 deletions
diff --git a/filter/qa/complex/filter/detection/typeDetection/Helper.java b/filter/qa/complex/filter/detection/typeDetection/Helper.java
new file mode 100644
index 000000000..23eb07fc4
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/Helper.java
@@ -0,0 +1,431 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+package complex.filter.detection.typeDetection;
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.io.NotConnectedException;
+import com.sun.star.io.XInputStream;
+
+import helper.StreamSimulator;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.ArrayList;
+
+import lib.TestParameters;
+import share.LogWriter;
+import util.PropertyName;
+import util.utils;
+
+
+
+/** Helper class for "TypeDetection"
+ * This class do file handling.
+ */
+public class Helper {
+
+ /** The runner log writer
+ * @member m_log for log purposes
+ * @member m_sTestDocPath directory for searching files to load
+ * @member m_vFiles list of all files described in "files.csv"
+ * @member m_hFileURLs contains the position of a file name in the m_vFiles Vector
+ * @member m_hFileTypes contains the position of a file type in the m_vFiles Vector
+ * @member m_param the test parameters
+ */
+
+ private final LogWriter m_log;
+
+ private final String m_sTestDocPath;
+
+ private final ArrayList<ArrayList<String>> m_vFiles;
+
+ private final HashMap<String,String> m_hFileURLs = new HashMap<String,String>();
+
+ private final HashMap<String,String> m_hFileTypes = new HashMap<String,String>();
+
+ private final TestParameters m_param;
+
+ /**
+ * construct a new instance of this class
+ * It creates the "todo" list and position lists for <code>URL</code> and
+ * and <code>Type</code> inside the "todo" list
+ *
+ * @param param the test parameters
+ *
+ * @param log the log writer
+ */
+
+ public Helper(TestParameters param, LogWriter log) {
+
+ m_param = param;
+ m_log = log;
+
+
+ // get all files from the given directory
+ m_sTestDocPath = (String)param.get("TestDocumentPath");
+
+ // get all files from "files.csv"
+ m_vFiles = getToDoList((String)m_param.get("csv.files"));
+
+ createFilesList();
+ }
+
+
+ /** Reads a comma separated file (CSV). Every line of the file is
+ * represented by an <code>Vector</code> entry. Every data entry of a row is
+ * also stored in a <code>Vector</code>. So the returned value is a
+ * <code>Vector[][]</code> where the first dimension represents a row
+ * and the second dimension includes the data values.
+ * @param csvFileName the name of the csv file
+ * @return Vector filled with Vector filled with data of a row
+ */
+ public ArrayList<ArrayList<String>> getToDoList(String csvFileName){
+
+ try {
+
+ ArrayList<ArrayList<String>> vAll = new ArrayList<ArrayList<String>>();
+ ArrayList<String> vFields = new ArrayList<String>();
+
+ // get content of file
+ ArrayList<String> content = getCSVFileContent(csvFileName);
+
+ // remove superfluous content like "#" started lines
+ content = removeSuperfluousContent(content);
+
+ // replace all place holders in file
+ content = replacePlaceHolder(content);
+
+ // create Enumeration
+ Iterator<String> contentEnum = content.iterator();
+
+ // the first line contains field names of the columns
+ // split line by ";"
+ StringTokenizer fields = new StringTokenizer(
+ contentEnum.next(),";");
+ int fieldCount = 0;
+ while (fields.hasMoreElements()){
+ vFields.add(fields.nextToken());
+ fieldCount++;
+ }
+
+ // fill vData with data of CSV-row
+ while (contentEnum.hasNext()){
+ ArrayList<String> vData = new ArrayList<String>();
+
+ StringTokenizer data = new StringTokenizer(
+ contentEnum.next(),";", true);
+
+ // example: data = "firstData;secondData;;forthData"
+ // => three tokens => missing one data because the imagine
+ // "thirdData" was not received by data.nextToken()
+ // Therefore here comes a special handling for empty data
+ boolean nextIsData = false;
+ int dataCount = 0;
+ while (data.hasMoreTokens()) {
+ String myToken = data.nextToken();
+ // if the "thirdData" will be received, myToken=";" but
+ // vData must add an empty String
+ if (myToken.equals(";")){
+ if (nextIsData ) {
+ vData.add("");
+ dataCount++;
+ nextIsData = false;
+ }
+ nextIsData = true;
+ } else {
+ vData.add(myToken);
+ dataCount++;
+ nextIsData = false;
+ }
+ }
+ for (int i=dataCount; i < fieldCount; i++) vData.add("");
+ vAll.add(vData);
+ }
+
+
+ return vAll;
+
+ } catch(ClassCastException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /** The csv files "files", "preselectedFilter", "preselectedType" and
+ * "serviceName" are delivered beside this class. This function seeks for
+ * the csv files and read them.
+ * @param csvFileName the name of the csv file
+ * @return a Vector containing the content of the file. <null/> if the file
+ * cannot be read
+ */
+
+ private ArrayList<String> getCSVFileContent(String csvFileName) {
+ try {
+ ArrayList<String> content = new ArrayList<String>();
+ BufferedReader br;
+ String line;
+ if ( m_param.getBool(PropertyName.DEBUG_IS_ACTIVE) ) {
+ System.out.println("Looking for "+csvFileName);
+ }
+
+ URL url = getClassURL(csvFileName);
+
+ if (url != null) {
+ URLConnection connection = url.openConnection();
+ InputStream in = connection.getInputStream();
+
+ br = new BufferedReader(new InputStreamReader(in));
+ try {
+ while( ( line = br.readLine() ) != null ) {
+ content.add( line );
+ }
+ } catch (IOException e) {
+ br.close();
+ return null;
+ }
+ br.close();
+ return content;
+ }
+
+ }catch (IOException e) {
+ }catch(NullPointerException e) {
+ return null;
+ }
+ return null;
+ }
+
+ /** returns a XInputStream of given file
+ * @param filePath the path to the file which should be loaded
+ * @return the XInputStream, <null/> if the
+ * file cannot be read
+ * @throws NotConnectedException was thrown if it was not possible to open <CODE>filePath</CODE>
+ */
+ public XInputStream getFileStream( String filePath )
+ throws NotConnectedException {
+ return new StreamSimulator(filePath, true, m_param);
+ }
+
+ /** replaces place holder in preselectedFilter.
+ * Because of filter names depend on StarOffice version like
+ * "StarOffice 6.0 Textdokument" or ""StarSuite 7 Textdokument"
+ * The filter names must be changed. The place holder will be replaced
+ * by an equivalent in "typeDetection.props"
+ * @param content the content of a csv file
+ * @return changed file content
+ */
+ private ArrayList<String> replacePlaceHolder(ArrayList<String> content){
+
+ ArrayList<String> vReturn = new ArrayList<String>();
+
+ ArrayList<String> placeHolders = new ArrayList<String>();
+ Iterator<String> paramsIter = m_param.keySet().iterator();
+ String placeHolder = (String)m_param.get("placeHolder");
+
+ // get all place holders from typeDetection.csv
+ while (paramsIter.hasNext()){
+ String holderKey = paramsIter.next();
+ if (holderKey.startsWith(placeHolder)){
+ placeHolders.add(holderKey);
+ }
+ }
+
+ // replace all occurrences of place holders in 'CSVData'
+ Iterator<String> cont = content.iterator();
+
+ while( cont.hasNext() ) {
+
+ String line = cont.next();
+ String newLine = line;
+ Iterator<String> holders = placeHolders.iterator();
+
+ while( holders.hasNext() ) {
+
+ String holder = holders.next();
+ int startPos = line.indexOf(holder);
+
+ if (startPos > -1){
+ try{
+ String holderValue = (String) m_param.get(holder);
+
+ newLine = newLine.substring(0,startPos) + holderValue +
+ newLine.substring(startPos + holder.length());
+
+ } catch (java.lang.IndexOutOfBoundsException e){
+ m_log.println("ERROR: problems while creating placeholder" +
+ " replaced list: "+ e);
+ }
+ }
+ }
+ vReturn.add(newLine);
+ }
+ return vReturn;
+ }
+
+ /** Removes lines of an ascii file content which starts with "#"
+ * or are empty
+ * @param content content of a csv file
+ * @return a stripped Vector
+ */
+ private ArrayList<String> removeSuperfluousContent(ArrayList<String> content){
+ ArrayList<String> newContent = new ArrayList<String>();
+ Iterator<String> cont = content.iterator();
+ while( cont.hasNext() ) {
+ String line = cont.next();
+ if (( ! line.startsWith( "#" ))&& ( line.length() != 0 )) {
+ newContent.add( line );
+ }
+ }
+ return newContent;
+ }
+
+ /** returns a <code>MediaDescriptor</code> filled with given properties and
+ * values.
+ * @param propNames String Array of property names
+ * @param values Object Array of property values
+ * @return <code>PropertyValue[]<code>
+ * @see com.sun.star.beans.PropertyValue
+ * @see com.sun.star.document.MediaDescriptor
+ */
+ public PropertyValue[] createMediaDescriptor(String[] propNames, Object[] values) {
+ PropertyValue[] props = new PropertyValue[propNames.length] ;
+
+ for (int i = 0; i < props.length; i++) {
+ props[i] = new PropertyValue() ;
+ props[i].Name = propNames[i] ;
+ if (values != null && i < values.length) {
+ props[i].Value = values[i] ;
+ }
+ }
+
+ return props ;
+ }
+
+ /** Appends system file separator if needed
+ * @param s the system path
+ * @return system path with ending system file separator
+ */
+ public String ensureEndingFileSep(String s){
+ if(s != null && !s.equals("") && !s.endsWith(File.separator)){
+ s = s.trim() + File.separator;
+ }else if(s == null)
+ s = "";
+ return s;
+ }
+
+ /** Returns the file URL for the given file name assembled by
+ * "TestDocumentPath" of typeDetection.props and "fileURL" of files.csv
+ * @param fileAlias the alias name of the file
+ * @return file URL
+ * @throws FileAliasNotFoundException was thrown if alias does not exist
+ */
+ public String getURLforfileAlias(String fileAlias)
+ throws FileAliasNotFoundException{
+ try{
+ String fileURL = m_hFileURLs.get(fileAlias).toString();
+ return utils.getFullURL(ensureEndingFileSep(m_sTestDocPath) + fileURL);
+ } catch (NullPointerException e){
+ throw new FileAliasNotFoundException(fileAlias, e);
+ }
+
+ }
+
+ /** Returns the file type for the given file name containing in files.csv
+ * @param fileAlias the alias name of the file
+ * @return file type
+ * @throws FileAliasNotFoundException was thrown if not alias was thrown
+ */
+ public String getTypeforfileAlias(String fileAlias)
+ throws FileAliasNotFoundException{
+ try{
+ return m_hFileTypes.get(fileAlias).toString();
+ } catch (NullPointerException e){
+ throw new FileAliasNotFoundException(fileAlias, e);
+ }
+ }
+
+ /**
+ * Fills the Hashtable m_hFileURLs with all file names and their URL
+ * and the Hashtable m_hFileTypes with all file names and their file
+ * type name. This information is extracted from "files.csv"
+ * This is for faster access to get fileURL and fileType of fileAlias
+ */
+ private void createFilesList(){
+ for (int i = 0; i < m_vFiles.size();i++){
+ ArrayList<String> toDo = m_vFiles.get(i);
+ m_hFileURLs.put(toDo.get(0), toDo.get(1));
+ m_hFileTypes.put(toDo.get(0), toDo.get(2));
+ }
+ }
+
+
+ /** Validate the returned file type for the file alias with the
+ * possible file types
+ * @param currentFileType the returned file type
+ * @param fileTypes all possible file types
+ * @return true if valid
+ */
+ public boolean checkFileType(String currentFileType, String fileTypes){
+
+ StringTokenizer data = new StringTokenizer(fileTypes,":", true);
+
+ boolean found = false;
+ while (data.hasMoreElements()) {
+
+ String actualFileType = data.nextElement().toString();
+
+ found = found || currentFileType.equals(actualFileType);
+ }
+ return found;
+ }
+
+ /** creates an input/output parameter of <code>PropertyValue[]<code>.
+ * @return PropertyValue[][]
+ * @param PropVal a PropertyValue
+ */
+ public PropertyValue[][] createInOutPropertyValue(PropertyValue[] PropVal){
+ PropertyValue[][] dummy = new PropertyValue[1][];
+ dummy[0] = PropVal;
+ return dummy;
+ }
+
+ private URL getClassURL(String fileName){
+ String PackagePath = this.getClass().getPackage().getName().replace('.','/');
+ return this.getClass().getResource("/" + PackagePath +"/" + fileName);
+ }
+
+ public String getClassURLString(String fileName){
+ return getClassURL(fileName).toString().replaceAll("file:","");
+ }
+
+
+}
+
+/** This exception should be thrown if a method seeks for an invalid alias name */
+class FileAliasNotFoundException extends java.lang.Exception{
+ /** throws error message with wrong alias name
+ * @param fileAlias the alias name
+ */
+ public FileAliasNotFoundException(String fileAlias, Throwable cause){
+ super("Could not get '"+fileAlias +"'", cause);
+ }
+}
diff --git a/filter/qa/complex/filter/detection/typeDetection/TypeDetection.java b/filter/qa/complex/filter/detection/typeDetection/TypeDetection.java
new file mode 100644
index 000000000..2c7a6934a
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/TypeDetection.java
@@ -0,0 +1,546 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package complex.filter.detection.typeDetection;
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.document.XTypeDetection;
+import com.sun.star.io.NotConnectedException;
+import com.sun.star.io.XInputStream;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import complexlib.ComplexTestCase;
+import java.io.File;
+
+import java.util.Iterator;
+import java.util.ArrayList;
+import util.utils;
+
+
+
+/** Check "TypeDetection"
+ * <p>
+ * This test will check the file type detection. This will be done by filling
+ * properties of a <code>MediaDescriptor</code>.
+ *
+ * In the test method <code>checkByURLonly</code> the
+ * <code>MediaDescriptor</code> was filled at once with the URL of a test
+ * file. At second it was filled with a <code>XInputStream</code> from test
+ * file. In both subtests the returned file type must match with an expected
+ * type.
+ *
+ * In the test method <code>checkPreselectedType</code> the
+ * <code>MediaDescriptor</code> was filled with the URL of the test file and
+ * with the name of a type which should be used. The returned type of the
+ * <code>TypeDetection<code> must match with an expected type.
+ *
+ * In the test method <code>checkPreselectedFilter</code> the
+ * <code>MediaDescriptor</code> was filled with the URL of the test file and
+ * with the name of a filter which should be used. The returned type of the
+ * <code>TypeDetection<code> must match with an expected type.
+ *
+ * In the test method <code>checkPreselectedDocService</code> the
+ * <code>MediaDescriptor</code> was filled with the URL of the test file and
+ * with the name of a document service which should be used. The returned type
+ * of the <code>TypeDetection<code> must match with an expected type.
+ *
+ *
+ * To get information on which test file should support which type, filter and
+ * document service, this information was collected from configuration files:
+ * <UL>
+ * <LI><a href="#TypeDetection.props">TypeDetection.props</a></LI>
+ * <LI><a href="#files.csv">files.csv</a></LI>
+ * <LI><a href="#preselectedFilter.csv">preselectedFilter.csv</a></LI>
+ * <LI><a href="#preselectedType.csv">preselectedType.csv</a></LI>
+ * <LI><a href="#serviceName.csv">serviceName.csv</a></LI>
+ * </UL>
+ * <p>
+ * <h3><A name="TypeDetection.props"></A>
+ * <code>typeDetection.props</code></h3>
+ * At first there will be the <code>typeDetection.props</code>. Here the following
+ * properties should be set (with example values):
+ *
+ * TestDocumentPath=file:///path/to/my/testdocuments
+ * placeHolder=%
+ * %SO6productname=StarOffice
+ * %SO6formatversion=6.0
+ *
+ * <code>TestDocumentPath</code>: this is the path to your test documents. If
+ * you have grouped your documents ie. by writer, calc, ... then it should be
+ * the root path. To specify the particular sub folders you have to use
+ * <code>csv.files</code>
+ * <p>
+ * <code>files.csv</code>: In this file all test documents are listed.
+ * Syntax: fileAlias;fileURL;defaultURLFileType;StreamFileTypes
+ * Example:
+ *
+ * Writer6_1;Writer/Writer6.sxw;writer_StarOffice_XML_Writer;writer_StarOffice_XML_Writer
+ * text1;Writer/Text.txt:generic_Text:generic_Text
+ *
+ * The first example shows you the following:
+ * <code>Writer6_1</code> is a free chosen name
+ * <code>Writer/Writer6.sxw</code> is the document path. This will be assembled
+ * by <code>TestDocumentPath</code> from <code>typeDetection.props</code>.
+ * <code>writer_StarOffice_XML_Writer</code>: this is the default file type of
+ * this file
+ *
+ * The second example displays two document types for
+ * <code>XInputStream</CODE> (<code>generic_Text</CODE>). These
+ * two document types are listed by a colon ':' as separator.
+ * This is needed because XInputStream can detect a text file as
+ * generic_Text.
+ * <p>
+ *
+ * <H3><A name="preselectedFilter.csv"</a>
+ * <code>preselectedFilter.csv</code></H3>
+ * In this file you can choose a special
+ * filter to detect the document. This makes sense ie. for csv-files: You can
+ * open csv files as Writer or as Calc. To check this case you have to specify
+ * in <code>csv.files</code> a fileAlias like ?csv_writer? and ?csv_calc? with
+ * the same fileURL and its specific defaultFileType.
+ * The returned file type by <code>TypeDetection</code> must be equal to the
+ * corresponding <code>defaultFileType</code> from <code>csv.files</code>
+ *
+ * Syntax: fileAlias;FilterName;FilterOptions;FilterData
+ * Example: Writer6_1;%SO6productname %SO6formatversion Textdocument;
+ *
+ * The example shows the following:
+ * <code>Writer6_1</code> is the same as in <code>csv.files</code>
+ * <code>%SO6productname %SO6formatversion Textdokument</code> is the filter
+ * name which should be used. Here we have a special: <code>%SO6productname
+ * %SO6formatversion</code> will be replaced by the equals of
+ * <code>typeDetection.props</code>. The filter names depends on the Office
+ * name and version. So a future Office could be called ?StarSuite 8?.
+ * <code>FilterOptions</code> is not relevant for this filter. But ie. for csv
+ * filter this entry could be used to specify the separator of the csv file.
+ * <code>FilterData<code> if filter needs some FilterData arguments you can
+ * specify them here
+ *
+ * <p>
+ * <H3><a name="preselectedType.csv"></A>
+ * <code>preselectedType.csv</code></H3>
+ * In this file you can preselect the type
+ * <code>TypeDetection</code> should use.
+ * The file type returned by <code>TypeDetection</code> must be equal to the
+ * preselected file type.
+ * Note: If you try to use invalid types you will get a failed test because
+ * <code>TypeDetection</code> tries to find out the type itself.
+ *
+ * Syntax: fileAlias;fileType
+ * Example: Writer6_1;writer_StarOffice_XML_Writer
+ *
+ * This example shows the following:
+ * <code>Writer6_1</code> is the same as in <code>csv.files</code>
+ * <code>writer_StarOffice_XML_Writer</code> is the file type which was used as
+ * parameter in <code>MediaDescriptor</code>. This type must be returned from
+ * <code>TypeDetection</code>
+ *
+ * <p>
+ * <H3><a name="serviceName.csv"></A>
+ * <code>serviceName.csv</code></H3> In this file you can preselect a service name
+ * to detect the file type. The file type returned by
+ * <code>TypeDetection</code> must be equal to the corresponding
+ * <code>defaultFileType</code> from <code>csv.files</code>
+ *
+ * Syntax: fileAlias;serviceName
+ * Example: Writer6_1;com.sun.star.text.FormatDetector
+ *
+ * This example shows the following:
+ * <code>Writer6_1</code> is the same as in <code>csv.files</code>
+ * <code>com.sun.star.text.FormatDetector</code> is the service name which was
+ * used as parameter in <code>MediaDescriptor</code>.
+ *
+ *
+ * <p>
+ * All these files will be copied by make file beside of
+ * <code>typeDetection.class</code>.
+ * @see com.sun.star.document.XTypeDetection
+ * @see com.sun.star.document.MediaDescriptor
+ */
+public class TypeDetection extends ComplexTestCase {
+
+ /**
+ * @member m_xDetection the object to test
+ * @member helper instance of helper class
+ */
+
+ static XTypeDetection m_xDetection;
+ static Helper helper = null;
+
+ /**
+ * A function to tell the framework, which test functions are available.
+ * @return All test methods.
+ */
+ @Override
+ public String[] getTestMethodNames() {
+ return new String[]{"checkByURLonly",
+ "checkPreselectedType",
+ "checkPreselectedFilter",
+ "checkPreselectedDocService",
+ "checkStreamLoader",
+ "checkStreamLoader"};
+
+ }
+
+ /** Create the environment for following tests.
+ * Use either a component loader from desktop or
+ * from frame
+ * @throws Exception Exception
+ */
+ public void before() throws Exception {
+
+ // create TypeDetection
+ XMultiServiceFactory xMSF = param.getMSF();
+ assure("Could not get XMultiServiceFactory", xMSF != null);
+
+ Object oInterface = xMSF.createInstance(
+ "com.sun.star.document.TypeDetection");
+
+ if (oInterface == null) {
+ failed("Service wasn't created") ;
+ }
+
+ XInterface oObj = (XInterface) oInterface ;
+ log.println("ImplName: "+utils.getImplName(oObj));
+
+ m_xDetection = UnoRuntime.queryInterface(XTypeDetection.class, oInterface);
+ Iterator<String> k = param.keySet().iterator();
+ while (k.hasNext()){
+ String kName = k.next();
+ log.println(kName + ":" + param.get(kName).toString());
+ }
+ // create instance of helper class
+ helper = new Helper(param, log);
+
+ }
+
+ /**
+ * close the environment
+ */
+ public void after() {
+ }
+
+ /**
+ * The <code>MediaDescriptor</code> was filled with the URL of a file. The
+ * <code>type</code> of the file is known and must be returned by
+ * <code>MediaDescriptor</code>
+ *
+ * Syntax of files.csv:
+ * fileAlias;fileURL;fileType
+ *
+ */
+ public void checkByURLonly() {
+ try{
+ log.println("### checkByURLonly() ###");
+ ArrayList<ArrayList<String>> CSVData = helper.getToDoList(
+ (String)param.get("csv.files"));
+ Iterator<ArrayList<String>> allToDos = CSVData.iterator();
+
+ while (allToDos.hasNext()){
+ ArrayList<String> toDo = allToDos.next();
+
+ String fileAlias = toDo.get(0);
+ String fileURL = toDo.get(1);
+ String URLfileType = toDo.get(2);
+ String StreamfileType = toDo.get(3);
+
+ fileURL = utils.getFullURL(helper.ensureEndingFileSep(
+ (String)param.get("TestDocumentPath")) + fileURL);
+
+ log.println("actual '"+ fileAlias +
+ "' ['" + URLfileType + "']: '" + fileURL);
+
+ checkMediaDescriptorURL(fileAlias, fileURL, URLfileType);
+ checkMediaDescriptorXInputStream(fileAlias, fileURL, StreamfileType);
+ }
+
+ } catch (ClassCastException e){
+ failed(e.toString(), ContinueWithTest.YES);
+ }
+ }
+
+ /** To check the <CODE>TypeDetection</CODE> by URL the <CODE>MediaDescriptor</CODE>
+ * was filled at first with the URL only, at second with <CODE>XInputStream</CODE>
+ * only. The <CODE>TypeDetection</CODE> must return the expected value
+ * @param fileAlias the alias name of the test file
+ * @param fileURL the URL of the test file
+ * @param fileType the expected type of the test file
+ * @see com.sun.star.document.MediaDescriptor
+ */
+ private void checkMediaDescriptorURL(
+ String fileAlias, String fileURL, String fileType){
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"URL"},
+ new Object[] {fileURL});
+ log.println("check only by URL...");
+
+ String type = m_xDetection.queryTypeByDescriptor(
+ helper.createInOutPropertyValue(MediaDescriptor), true);
+
+ boolean fileTypeOK = helper.checkFileType(type, fileType);
+
+ assure("\nURL-test : " + fileAlias + ":\n\treturned type: '" + type +
+ "'\n\texpected type: '" + fileType + "'",fileTypeOK ,ContinueWithTest.YES);
+ }
+
+ /** Fills a MediaDescriptor with a <code>XInputStream</code> of the test
+ * file given by URL.
+ * Then the MediaDescriptor was used as parameter for TypeDetection.
+ * The TypeDetection must return expected type
+ * @param fileAlias the alias name of the test file
+ * @param fileURL the URL of the test file
+ * @param fileType the expected type of the test file
+ * @see com.sun.star.document.MediaDescriptor
+ * @see com.sun.star.io.XInputStream
+ */
+ private void checkMediaDescriptorXInputStream(
+ String fileAlias, String fileURL, String fileType){
+
+ XInputStream xStream = null;
+
+ try{
+ xStream = helper.getFileStream( fileURL );
+ } catch (NotConnectedException e) {
+ failed("Could not get XInputStream from file :'" + fileURL + "'",ContinueWithTest.YES);
+ return;
+ }
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"InputStream"},
+ new Object[] {xStream});
+ log.println("check only by XInputStream...");
+
+ String type = m_xDetection.queryTypeByDescriptor(
+ helper.createInOutPropertyValue(MediaDescriptor), true);
+
+ boolean fileTypeOK = helper.checkFileType(type, fileType);
+
+ assure("\nXInputStream-test: " + fileAlias + ":\n\treturned type: '" + type +
+ "'\n\texpected type: '" + fileType + "'", fileTypeOK, ContinueWithTest.YES);
+
+ }
+
+ /**
+ * The <code>MediaDescriptor</code> was filled with the URL of a file. The
+ * <code>type</code> of the file is known and must be returned by
+ * <code>MediaDescriptor</code>
+ *
+ * Syntax of files.csv:
+ * fileAlias;fileURL;fileType
+ *
+ */
+ public void checkPreselectedType() {
+ try{
+ log.println("### checkPreselectedType() ###");
+
+ ArrayList<ArrayList<String>> CSVData = helper.getToDoList(
+ (String)param.get("csv.preselectedType"));
+ Iterator<ArrayList<String>> allToDos = CSVData.iterator();
+
+ while (allToDos.hasNext()){
+ try{
+ ArrayList<String> toDo = allToDos.next();
+
+ String fileAlias = toDo.get(0);
+ String fileURL = helper.getURLforfileAlias(fileAlias);
+ String preselectFileType = toDo.get(1);
+ String expectedFileType = toDo.get(2);
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"URL", "MediaType"},
+ new Object[] {fileURL, preselectFileType});
+ log.println("check '" + fileAlias + "' with MediaType: '" +
+ preselectFileType + "'");
+
+ String type = m_xDetection.queryTypeByDescriptor(
+ helper.createInOutPropertyValue(MediaDescriptor), true);
+
+ boolean fileTypeOK = helper.checkFileType(type, expectedFileType);
+
+ assure("\n" + fileAlias + ":\n\treturned type: '" + type +
+ "'\n\texpected type: '" + expectedFileType + "'",
+ fileTypeOK, ContinueWithTest.YES);
+
+ } catch (FileAliasNotFoundException e){
+ failed(e.toString(),ContinueWithTest.YES);
+ }
+
+ }
+
+ } catch (ClassCastException e){
+ failed(e.toString(), ContinueWithTest.YES);
+ }
+ }
+
+
+ /**
+ * Check loading from a stream. The source for the stream is the
+ * first fileAlias that matches "*.txt" in the file list
+ * of the given directory.
+ */
+ public void checkPreselectedFilter() {
+ try{
+ log.println("### checkPreselectedFilter() ###");
+
+ ArrayList<ArrayList<String>> CSVData = helper.getToDoList(
+ (String)param.get("csv.preselectedFilter"));
+ Iterator<ArrayList<String>> allToDos = CSVData.iterator();
+
+ while (allToDos.hasNext()){
+ try{
+ ArrayList<String> toDo = allToDos.next();
+
+ String fileAlias = toDo.get(0);
+ String fileURL = helper.getURLforfileAlias(fileAlias);
+ String filterName = toDo.get(1);
+ String filterOptions = toDo.get(2);
+ String filterData = toDo.get(3);
+ String expectedType = toDo.get(4);
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"URL","FilterName",
+ "FilterOptions","FilterData"},
+ new Object[] {fileURL, filterName,
+ filterOptions, filterData});
+
+ log.println("check '" + fileAlias + "' with filter: '" +
+ filterName + "'");
+
+ String type = m_xDetection.queryTypeByDescriptor(
+ helper.createInOutPropertyValue(MediaDescriptor), true);
+
+ boolean fileTypeOK = helper.checkFileType(type, expectedType);
+
+ assure("\n" + fileAlias + ":\n\treturned type: '" + type +
+ "'\n\texpected type: '" + expectedType + "'",
+ fileTypeOK,ContinueWithTest.YES);
+
+ } catch (FileAliasNotFoundException e){
+ failed(e.toString(),ContinueWithTest.YES);
+ }
+
+ }
+
+ } catch (ClassCastException e){
+ failed(e.toString(), ContinueWithTest.YES);
+ }
+ }
+
+ /**
+ * Check URL encoding. The first fileAlias that matches "*.sxw"
+ * is used as source for several encodings.
+ */
+ public void checkPreselectedDocService() {
+ try{
+ log.println("### checkPreselectedDocService() ###");
+
+ ArrayList<ArrayList<String>> CSVData = helper.getToDoList((String)param.get("csv.serviceName"));
+ Iterator<ArrayList<String>> allToDos = CSVData.iterator();
+
+ while (allToDos.hasNext()){
+ try{
+ ArrayList<String> toDo = allToDos.next();
+
+ String fileAlias = toDo.get(0);
+ String fileURL = helper.getURLforfileAlias(fileAlias);
+ String serviceName = toDo.get(1);
+ String fileType = helper.getTypeforfileAlias(fileAlias);
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"URL", "DocumentService"},
+ new Object[] {fileURL, serviceName});
+ log.println("check " + fileAlias);
+
+ String type = m_xDetection.queryTypeByDescriptor(
+ helper.createInOutPropertyValue(MediaDescriptor), true);
+
+ boolean fileTypeOK = helper.checkFileType(type, fileType);
+
+ assure("\n" + fileAlias + ":\n\treturned type: '" + type +
+ "'\t\nexpected type: '" + fileType + "'",
+ fileTypeOK, ContinueWithTest.YES);
+
+ } catch (FileAliasNotFoundException e){
+ failed(e.toString(),ContinueWithTest.YES);
+ }
+
+ }
+
+ } catch (ClassCastException e){
+ failed(e.toString(), ContinueWithTest.YES);
+ }
+ }
+
+ public void checkStreamLoader(){
+ try{
+
+ /*
+ * As files, use the typeDetection.props and one of the csv files.
+ * Those can, via dmake, simply set rights on others.
+ *
+ */
+ log.println("### checkStreamLoader() ###");
+ String[] urls = new String[] {
+ helper.getClassURLString("TypeDetection.props"),
+ helper.getClassURLString("files.csv") };
+
+ for (int j=0; j<urls.length; j++){
+ String fileURL = urls[j];
+ File file = new File(fileURL);
+ fileURL = utils.getFullURL(fileURL);
+
+ PropertyValue[] MediaDescriptor = helper.createMediaDescriptor(
+ new String[] {"URL"},
+ new Object[] {fileURL});
+
+ if (file.canWrite()) log.println("check writable file...");
+ else log.println("check readonly file...");
+
+ PropertyValue[][] inOut = helper.createInOutPropertyValue(MediaDescriptor);
+ PropertyValue[] in = inOut[0];
+ log.println("in-Parameter:");
+ for (int i=0; i < in.length; i++){
+ log.println("["+i+"] '" + in[i].Name + "':'" + in[i].Value.toString()+"'");
+ }
+
+ String type = m_xDetection.queryTypeByDescriptor(inOut, true);
+
+ PropertyValue[] out = inOut[0];
+
+ log.println("out-Parameter");
+ boolean bReadOnly = false;
+ for (int i=0; i < out.length; i++){
+ if ((out[i].Name.equals("ReadOnly")) && (out[i].Value.toString().equals("true"))) bReadOnly = true;
+ log.println("["+i+"] '" + out[i].Name + "':'" + out[i].Value.toString()+"'");
+ }
+
+ if (file.canWrite() && bReadOnly)
+ assure("\nStreamLoader: file '"+ fileURL +"' is writable but out-Parameter does contain 'ReadOnly' property", false, ContinueWithTest.YES);
+ else if ((!file.canWrite()) && (!bReadOnly))
+ assure("\nStreamLoader: file '"+ fileURL +"'is readonly but out-Parameter does not contain 'ReadOnly' property", false, ContinueWithTest.YES);
+ else assure("all ok", true, ContinueWithTest.YES);
+
+ }
+
+ } catch (ClassCastException e){
+ failed(e.toString(), ContinueWithTest.YES);
+ }
+
+ }
+}
diff --git a/filter/qa/complex/filter/detection/typeDetection/TypeDetection.props b/filter/qa/complex/filter/detection/typeDetection/TypeDetection.props
new file mode 100644
index 000000000..af845dd00
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/TypeDetection.props
@@ -0,0 +1,31 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# 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 .
+#
+
+# UNIX:
+#TestDocumentPath=file:///net/margritte/usr/qaapi/dev/cws/filtercfg/docTypes
+# WINDOWS
+TestDocumentPath=//margritte/qaapi/dev/cws/filtercfg/docTypes
+
+csv.files=files.csv
+csv.preselectedFilter=preselectedFilter.csv
+csv.preselectedType=preselectedType.csv
+csv.serviceName=serviceName.csv
+csv.streamFileTypes=streamFileTypes.csv
+placeHolder=%
+%SO6productname=StarOffice
+%SO6formatversion=6.0
diff --git a/filter/qa/complex/filter/detection/typeDetection/files.csv b/filter/qa/complex/filter/detection/typeDetection/files.csv
new file mode 100644
index 000000000..8e558590b
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/files.csv
@@ -0,0 +1,117 @@
+fileAlias;fileURL;defaultFileType;streamFileType
+#**************************************************************
+#** N O T E
+#**
+#** The detection for template filter and writer_text_encoded
+#** will be changed on following childworkspace
+#**
+#**************************************************************
+###################################
+# W R I T E R D O C U M E N T S
+#################################
+rtf1;Writer/AoE2a.rtf;writer_Rich_Text_Format;writer_Rich_Text_Format;writer_Rich_Text_Format
+text1;Writer/Text_DOS.txt;generic_Text;generic_Text
+Word2000_document;Writer/Word2000.doc;writer_MS_Word_97;writer_MS_Word_97
+Word2000_template;Writer/Word2000_template.dot;writer_MS_Word_97_Vorlage;writer_MS_Word_97_Vorlage
+Word6_document;Writer/Word6.doc;writer_MS_WinWord_60;writer_MS_WinWord_60
+Word6_template;Writer/Word6_template.dot;writer_MS_Word_95_Vorlage;writer_MS_Word_95_Vorlage
+Word95_document;Writer/Word95.doc;writer_MS_WinWord_60;writer_MS_WinWord_60
+Word97_document;Writer/Word97.doc;writer_MS_Word_97;writer_MS_Word_97
+Word97_template;Writer/Word97_template.dot;writer_MS_Word_97_Vorlage;writer_MS_Word_97_Vorlage
+WordXP_document;Writer/WordXP.doc;writer_MS_Word_97;writer_MS_Word_97
+WordXP_template;Writer/WordXP_template.dot;writer_MS_Word_97_Vorlage;writer_MS_Word_97_Vorlage
+Writer3_document;Writer/Writer3.sdw;writer_StarWriter_30;writer_StarWriter_30_VorlageTemplate
+Writer3_template;Writer/Writer3_template.vor;writer_StarWriter_30_VorlageTemplate;writer_StarWriter_30_VorlageTemplate
+Writer4_document;Writer/Writer4.sdw;writer_StarWriter_40;writer_StarWriter_40_VorlageTemplate
+Writer4_template;Writer/Writer4_template.vor;writer_StarWriter_40_VorlageTemplate;writer_StarWriter_40_VorlageTemplate
+Writer5_document;Writer/Writer5.sdw;writer_StarWriter_50;writer_StarWriter_50_VorlageTemplate
+Writer5_template;Writer/Writer5_template.vor;writer_StarWriter_50_VorlageTemplate;writer_StarWriter_50_VorlageTemplate
+Writer6_document;Writer/Writer6.sxw;writer_StarOffice_XML_Writer;writer_StarOffice_XML_Writer
+Writer6_template;Writer/Writer6_template.stw;writer_StarOffice_XML_Writer_Template;writer_StarOffice_XML_Writer
+Writer6_html;Writer/WriterWeb.html;generic_HTML;generic_HTML
+ApiPro3;Writer/AmiPro_3x.sam;generic_Text;generic_Text
+Applix;Writer/Applix.aw;generic_Text;generic_Text
+Text2;Writer/Counterstrike I.txt;generic_Text;generic_Text
+FrameMake_document;Writer/FrameMaker.mif;generic_Text;generic_Text
+WordPro_Hangul;Writer/HangulWordPro.hwp;generic_Text;generic_Text
+Ichitaro;Writer/Ichitaro.jtd;writer_JustSystem_Ichitaro_10;writer_JustSystem_Ichitaro_10
+MacWord5_document;Writer/MacWord_5.mcw;generic_Text;generic_Text
+rtf2;Writer/RTF.rtf;writer_Rich_Text_Format;writer_Rich_Text_Format
+wps2000_document;Writer/WPS_2000.wps;writer_WPSSystem_WPS2000_10;writer_WPSSystem_WPS2000_10
+WinWord2_document;Writer/WinWord_2x.doc;generic_Text;generic_Text
+WinWord6_95_document;Writer/WinWord_6_95.doc;writer_MS_WinWord_60;writer_MS_WinWord_60
+WinWord97_2000_XP_document;Writer/Winword_97_2000_xp.doc;writer_MS_Word_97;writer_MS_Word_97
+WordPerfect;Writer/WordPerfect.wpd;generic_Text;generic_Text
+csv2;Writer/Text_CSV.txt;generic_Text;generic_Text
+###################################
+# C A L C D O C U M E N T S
+##################################
+csv1;Calc/Text_CSV.csv;generic_Text;generic_Text
+Calc1;Calc/Calc1.SDC;calc_StarCalc_10;calc_StarCalc_10
+Calc2_document;Calc/Calc3.SDC;calc_StarCalc_30_VorlageTemplate;calc_StarCalc_30
+Calc2_template;Calc/Calc3.vor;calc_StarCalc_30;calc_StarCalc_30
+Calc4_document;Calc/Calc4.sdc;calc_StarCalc_40;calc_StarCalc_40
+Calc4_template;Calc/Calc4.vor;calc_StarCalc_40_VorlageTemplate;calc_StarCalc_40
+Calc5_document;Calc/Calc5.sdc;calc_StarCalc_50;calc_StarCalc_50
+Calc5_template;Calc/Calc5.vor;calc_StarCalc_50_VorlageTemplate;calc_StarCalc_50
+Calc6_document;Calc/Calc_6.stc;calc_StarOffice_XML_Calc;calc_StarOffice_XML_Calc
+Calc6_template;Calc/Calc_6.sxc;calc_StarOffice_XML_Calc_Template;calc_StarOffice_XML_Calc
+dif;Calc/DIF.DIF;calc_DIF;generic_Text
+Excel2;Calc/Excel2.XLS;calc_MS_Excel_40;calc_MS_Excel_40
+Excel3;Calc/Excel3.XLS;calc_MS_Excel_40;calc_MS_Excel_40
+Excel4_document;Calc/Excel4.XLS;calc_MS_Excel_40;calc_MS_Excel_40
+Excel4_template;Calc/Excel4.XLT;calc_MS_Excel_40_VorlageTemplate;calc_MS_Excel_40
+Exel95_document;Calc/Excel5_95.XLS;calc_MS_Excel_5095;calc_MS_Excel_5095
+Exel95_template;Calc/Excel5_template.XLT;calc_MS_Excel_5095;calc_MS_Excel_5095
+Excel97_document;Calc/Excel97_2000_XP.xls;calc_MS_Excel_97;calc_MS_Excel_97
+#Excel97_1dummy;Calc/Excel97_2000_XP.dummy;calc_MS_Excel_97;calc_MS_Excel_97
+Excel97_template;Calc/Excel97_2000_XP.xlt;calc_MS_Excel_97;calc_MS_Excel_97
+Calc_html;Calc/HTML_Calc.html;generic_HTML;generic_HTML
+Calc_rtf;Calc/RTF_StarOffice_Calc.rtf;writer_Rich_Text_Format;writer_Rich_Text_Format
+slk;Calc/SYLK.SLK;calc_SYLK;generic_Text
+Webpagequery;Calc/Webpagequery.html;generic_HTML;generic_HTML
+dbase;Calc/dbase.DBF;calc_dBase;generic_Text
+######################################################
+# D R A W
+######################################################
+draw6_document;Draw/draw1.sxd;draw_StarOffice_XML_Draw;draw_StarOffice_XML_Draw
+draw6_template;Draw/draw2.std;draw_StarOffice_XML_Draw_Template;draw_StarOffice_XML_Draw
+draw5_document;Draw/draw3.sda;draw_StarDraw_50;draw_StarDraw_50_Vorlage
+draw5_template;Draw/draw4.vor;draw_StarDraw_50_Vorlage;draw_StarDraw_50_Vorlage
+draw3_document;Draw/draw5.sdd;draw_StarDraw_30;draw_StarDraw_30
+draw3_template;Draw/draw6.vor;draw_StarDraw_30;draw_StarDraw_30
+######################################################
+# I M P R E S S
+######################################################
+Impress6_document;Impress/imp1.sxi;impress_StarOffice_XML_Impress;impress_StarOffice_XML_Impress
+Impress4_document;Impress/imp10.sdd;impress_StarImpress_40;impress_StarImpress_40
+Impress4_template;Impress/imp11.vor;iimpress_StarImpress_40_Vorlage;impress_StarImpress_40
+Impress6_template;Impress/imp2.sti;impress_StarOffice_XML_Impress_Template;impress_StarOffice_XML_Impress
+PowerPoint97_document;Impress/imp3.ppt;impress_MS_PowerPoint_97;impress_MS_PowerPoint_97
+PowerPoint97_template;Impress/imp4.pot;impress_MS_PowerPoint_97_Vorlage;impress_MS_PowerPoint_97
+DrawImpress6_document;Impress/imp5.sxd;draw_StarOffice_XML_Draw;draw_StarOffice_XML_Draw
+DrawImpress5_document;Impress/imp6.sda;draw_StarDraw_50;draw_StarDraw_50_Vorlage
+DrawImpress3_document;Impress/imp7.sdd;draw_StarDraw_30;draw_StarDraw_30
+Impress5_document;Impress/imp8.sdd;impress_StarImpress_50;impress_StarImpress_50
+Impress5_template;Impress/imp9.vor;impress_StarImpress_50_Vorlage;impress_StarImpress_50
+#######################################################
+# G R A P H I C S
+######################################################
+bitmap;Graphics/pic.bmp;bmp_MS_Windows;bmp_MS_Windows
+emf;Graphics/pic.emf;emf_MS_Windows_Metafile;emf_MS_Windows_Metafile:generic_Text
+eps;Graphics/pic.eps;eps_Encapsulated_PostScript;eps_Encapsulated_PostScript
+gif;Graphics/pic.gif;gif_Graphics_Interchange;gif_Graphics_Interchange
+jpg;Graphics/pic.jpg;jpg_JPEG;jpg_JPEG
+met;Graphics/pic.met;met_OS2_Metafile;met_OS2_Metafile:generic_Text
+pbm;Graphics/pic.pbm;pbm_Portable_Bitmap;pbm_Portable_Bitmap
+pct;Graphics/pic.pct;pct_Mac_Pict;pct_Mac_Pict
+pgm;Graphics/pic.pgm;pgm_Portable_Graymap;pgm_Portable_Graymap
+png;Graphics/pic.png;png_Portable_Network_Graphic;png_Portable_Network_Graphic
+ppm;Graphics/pic.ppm;ppm_Portable_Pixelmap;ppm_Portable_Pixelmap
+ras;Graphics/pic.ras;ras_Sun_Rasterfile;ras_Sun_Rasterfile
+svm;Graphics/pic.svm;svm_StarView_Metafile;svm_StarView_Metafile:generic_Text
+tif;Graphics/pic.tif;tif_Tag_Image_File;tif_Tag_Image_File:generic_Text
+webp;Graphics/pic.webp;webp_WebP;webp_WebP
+wmf;Graphics/pic.wmf;wmf_MS_Windows_Metafile;wmf_MS_Windows_Metafile:generic_Text
+
+
diff --git a/filter/qa/complex/filter/detection/typeDetection/makefile.mk b/filter/qa/complex/filter/detection/typeDetection/makefile.mk
new file mode 100644
index 000000000..94dca18d6
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/makefile.mk
@@ -0,0 +1,109 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# 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 .
+#
+PRJ = ..$/..$/..$/..$/..
+PRJNAME = filter
+TARGET = TypeDetection
+PACKAGE = complex$/filter$/detection$/typeDetection
+
+# --- Settings -----------------------------------------------------
+.INCLUDE: settings.mk
+
+
+#----- compile .java files -----------------------------------------
+
+JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar
+JAVAFILES = TypeDetection.java Helper.java
+JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class)
+
+#----- make a jar from compiled files ------------------------------
+
+MAXLINELENGTH = 100000
+
+JARCLASSDIRS = $(PACKAGE)
+JARTARGET = $(TARGET).jar
+JARCOMPRESS = TRUE
+
+# --- Parameters for the test --------------------------------------
+
+# start an office if the parameter is set for the makefile
+.IF "$(OFFICE)" == ""
+CT_APPEXECCOMMAND =
+.ELSE
+CT_APPEXECCOMMAND = -AppExecutionCommand "$(OFFICE)$/soffice --accept=socket,host=localhost,port=8100;urp;"
+.ENDIF
+
+# test base is java complex
+CT_TESTBASE = -TestBase java_complex
+
+# test looks something like the.full.package.TestName
+CT_TEST = -o $(PACKAGE:s\$/\.\).TypeDetection
+
+# start the runner application
+CT_APP = org.openoffice.Runner
+
+# --- Targets ------------------------------------------------------
+
+.IF "$(depend)" == ""
+ CHMOD $(CLASSDIR)$/$(PACKAGE)$/TypeDetection.props \
+ $(CLASSDIR)$/$(PACKAGE)$/preselectedFilter.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/preselectedType.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/serviceName.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/files.csv : ALLTAR
+.ELSE
+ CHMOD $(CLASSDIR)$/$(PACKAGE)$/TypeDetection.props \
+ $(CLASSDIR)$/$(PACKAGE)$/preselectedFilter.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/preselectedType.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/serviceName.csv \
+ $(CLASSDIR)$/$(PACKAGE)$/files.csv : ALLDEP
+.ENDIF
+
+.INCLUDE : target.mk
+
+$(CLASSDIR)$/$(PACKAGE)$/preselectedFilter.csv : preselectedFilter.csv
+ cp preselectedFilter.csv $(CLASSDIR)$/$(PACKAGE)$/preselectedFilter.csv
+ jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/preselectedFilter.csv
+
+$(CLASSDIR)$/$(PACKAGE)$/preselectedType.csv : preselectedType.csv
+ cp preselectedType.csv $(CLASSDIR)$/$(PACKAGE)$/preselectedType.csv
+ jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/preselectedType.csv
+
+$(CLASSDIR)$/$(PACKAGE)$/serviceName.csv : serviceName.csv
+ cp serviceName.csv $(CLASSDIR)$/$(PACKAGE)$/serviceName.csv
+ jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/serviceName.csv
+
+$(CLASSDIR)$/$(PACKAGE)$/files.csv : files.csv
+ cp files.csv $(CLASSDIR)$/$(PACKAGE)$/files.csv
+ jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/files.csv
+
+$(CLASSDIR)$/$(PACKAGE)$/TypeDetection.props : TypeDetection.props
+ cp TypeDetection.props $(CLASSDIR)$/$(PACKAGE)$/TypeDetection.props
+ jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/TypeDetection.props
+
+# --- chmod --------------------------------------------------------
+
+CHMOD :
+ chmod 444 $(CLASSDIR)$/$(PACKAGE)$/*.csv
+ chmod 666 $(CLASSDIR)$/$(PACKAGE)$/*.props
+
+RUN: run
+
+run:
+ java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_TEST)
+
+
+
diff --git a/filter/qa/complex/filter/detection/typeDetection/preselectedFilter.csv b/filter/qa/complex/filter/detection/typeDetection/preselectedFilter.csv
new file mode 100644
index 000000000..ccf4d864c
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/preselectedFilter.csv
@@ -0,0 +1,6 @@
+fileAlias;FilterName;FilterOptions;FilterData;expectedFileType
+text1;%SO6productname %SO6formatversion Textdokument;;;generic_Text
+csv1;%SO6productname %SO6formatversion Textdokument;;;generic_Text
+csv1;Text - txt - csv (StarCalc);;;generic_Text
+csv2;%SO6productname %SO6formatversion Textdokument;;;generic_Text
+csv2;Text - txt - csv (StarCalc);;;generic_Text
diff --git a/filter/qa/complex/filter/detection/typeDetection/preselectedType.csv b/filter/qa/complex/filter/detection/typeDetection/preselectedType.csv
new file mode 100644
index 000000000..07abce185
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/preselectedType.csv
@@ -0,0 +1,6 @@
+fileAlias;preselectFileType;expectFileType
+csv1;generic_Text;generic_Text
+csv1;generic_Text;generic_Text
+#csv2;generic_Text
+#csv2;generic_Text
+text1;generic_Text;generic_Text \ No newline at end of file
diff --git a/filter/qa/complex/filter/detection/typeDetection/serviceName.csv b/filter/qa/complex/filter/detection/typeDetection/serviceName.csv
new file mode 100644
index 000000000..1e174fa85
--- /dev/null
+++ b/filter/qa/complex/filter/detection/typeDetection/serviceName.csv
@@ -0,0 +1,6 @@
+fileAlias;serviceName;expectedType
+text1;com.sun.star.text.FormatDetector
+csv1;com.sun.star.text.FormatDetector;generic_Text
+csv1;com.sun.star.comp.filters.PlainTextFilterDetect;generic_Text
+csv2;com.sun.star.text.FormatDetector;generic_Text
+csv2;com.sun.star.comp.filters.PlainTextFilterDetect;generic_Text
diff --git a/filter/qa/complex/filter/misc/FinalizedMandatoryTest.java b/filter/qa/complex/filter/misc/FinalizedMandatoryTest.java
new file mode 100644
index 000000000..28a71aaab
--- /dev/null
+++ b/filter/qa/complex/filter/misc/FinalizedMandatoryTest.java
@@ -0,0 +1,304 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package complex.filter.misc;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.container.XNameReplace;
+import com.sun.star.lang.WrappedTargetRuntimeException;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.util.XFlushable;
+
+
+/**
+ * This complex test checks the functionality of the properties "Finalized" and "Mandatory" of
+ * the services <CODE>com.sun.star.document.FilterFactory</CODE> and
+ * <CODE>com.sun.star.document.TypeDetection</CODE>.
+ *
+ * Each of these services represent a container of <CODE>PropertyValue[]</CODE>.
+ * The <CODE>PropertyValue[]</CODE> contains among others the properties called
+ * <CODE>Finalized</CODE> and <CODE>Mandatory</CODE>. If the property
+ * <CODE>Finalized</CODE> is set to <CODE>true</CODE>, a filter can be removed
+ * but will not be able to be changed.
+ * If the property <CODE>Mandatory</CODE> is set to <CODE>true</CODE>, the filter
+ * can not be removed.
+ *
+ * Every filter, which is registered to the office, will be tested. For every filter-test
+ * a new instance of the mentioned services will be created.
+
+ * During the test the property <CODE>UIName</CODE>
+ * will be changed and the service will be flushed. The test checks for expected exceptions:
+ * If the property <CODE>Finalized</CODE> equals
+ * <CODE>true</CODE> the tests check if an <CODE>Exception</CODE> must be thrown.
+ * The next step of the test is the removal of the filter was removed, then the service
+ * will be flushed. The test checks for expected exceptions: If the property
+ * <CODE>Mandatory</CODE> equals <CODE>true</CODE>, an <CODE>Exception</CODE> must
+ * be thrown.
+ * This test results <CODE>false</CODE> state if there is no filter available with:
+ * <CODE>Finalized=true</CODE>
+ * <CODE>Finalized=false</CODE>
+ * <CODE>Mandatory=true</CODE>
+ * <CODE>Mandatory=false</CODE>
+ */
+public class FinalizedMandatoryTest
+{
+
+ XMultiServiceFactory xMSF;
+
+ /** Create the environment for following tests.
+ * Use either a component loader from desktop or
+ * from frame
+ * @throws Exception Exception
+ */
+ @Before public void before() throws Exception
+ {
+
+ // create TypeDetection
+ xMSF = getMSF();
+ assertNotNull("Could not get XMultiServiceFactory", xMSF);
+
+ }
+
+ /**
+ * Creates an instance for the given <CODE>serviceName</CODE>
+ * @param serviceName the name of the service which should be created
+ * @throws Exception was thrown if creation fails
+ * @return <CODE>XInterface</CODE> of service
+ */
+ private XInterface getTestObject(String serviceName) throws Exception
+ {
+
+ Object oInterface = xMSF.createInstance(serviceName);
+
+ assertNotNull("Service wasn't created", oInterface);
+ return (XInterface) oInterface;
+ }
+
+ /**
+ * call the function <CODE>checkReadonlySupport</CODE> to test <CODE>com.sun.star.document.FilterFactory</CODE>
+ * @see com.sun.star.document.FilterFactory
+ */
+ @Test public void checkReadonlySupportFilterFactory() throws Exception
+ {
+ checkReadonlySupport("com.sun.star.document.FilterFactory");
+ }
+
+ /*
+ * call the function <CODE>checkReadonlySupport</CODE> to test <CODE>com.sun.star.document.TypeDetection</CODE>
+ * @see com.sun.star.document.TypeDetection
+ */
+ @Test public void checkReadonlySupportTypeDetection() throws Exception
+ {
+ checkReadonlySupport("com.sun.star.document.TypeDetection");
+ }
+
+ /*
+ * test the given service <CODE>serviceName</CODE>.
+ * For every filter a new instance was created and the tests started.
+ * @param serviceName the name of the service to test
+ */
+ private void checkReadonlySupport(String serviceName) throws Exception
+ {
+ System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ System.out.println("testing service '" + serviceName + "'");
+
+ XInterface oObj = null;
+ oObj = getTestObject(serviceName);
+ System.out.println("ImplName: " + util.utils.getImplName(oObj));
+
+ boolean mandatoryTrue = false;
+ boolean mandatoryFalse = false;
+ boolean finalizedTrue = false;
+ boolean finalizedFalse = false;
+
+
+ XNameAccess xNA = UnoRuntime.queryInterface(XNameAccess.class, oObj);
+ String[] filterNames = xNA.getElementNames();
+
+ // XNameContainer; XNameReplace
+ String filterName = filterNames[0];
+ Object[] instance = null;
+
+ for (int i = 0; i < filterNames.length; i++)
+ {
+ System.out.println("------------------------------------------------");
+ filterName = filterNames[i];
+ System.out.println(filterName);
+
+ // testobject must new created for every test.
+ // We change in a loop the container and try to flush this changes.
+ // If we get an expected exception this container is corrupt. It's
+ // similar to a document which could not be saved because of invalid
+ // content. While you don't remove the invalid content you will never
+ // be able to save the document. Same here.
+ oObj = getTestObject(serviceName);
+
+ xNA = UnoRuntime.queryInterface(XNameAccess.class, oObj);
+ XNameContainer xNC = UnoRuntime.queryInterface(XNameContainer.class, oObj);
+ XNameReplace xNR = UnoRuntime.queryInterface(XNameReplace.class, oObj);
+ XFlushable xFlush = UnoRuntime.queryInterface(XFlushable.class, oObj);
+
+ instance = (Object[]) xNA.getByName(filterName);
+ PropertyValue[] props = (PropertyValue[]) instance;
+
+ printPropertyValues(props);
+
+ boolean isMandatory = ((Boolean) getPropertyValueValue(props, "Mandatory")).booleanValue();
+ boolean isFinalized = ((Boolean) getPropertyValueValue(props, "Finalized")).booleanValue();
+
+ // memory if every state is available
+ mandatoryTrue |= isMandatory;
+ mandatoryFalse |= !isMandatory;
+
+ finalizedTrue |= isFinalized;
+ finalizedFalse |= !isFinalized;
+
+ //change the filter
+ setPropertyValueValue((PropertyValue[]) instance, "UIName", "dummy");
+
+ // 1a.) try to change the filter in the container
+ xNR.replaceByName(filterName, instance);
+
+ // 1b.) try to write the changed filter to the configuration.
+ // This must result in an exception if the filter is finalized.
+ boolean flushError = false;
+ try
+ {
+ xFlush.flush();
+ }
+ catch (WrappedTargetRuntimeException e)
+ {
+ flushError = true;
+ assertTrue("Unexpected exception while flushing changed filter '" + filterName + "'", isFinalized);
+ }
+ assertTrue("Expected exception was not thrown while flushing changed filter '" + filterName + "' Finalized:" + isFinalized,
+ !(flushError ^ isFinalized));
+
+
+
+ // 2a.) try to remove the filter from the container
+ xNC.removeByName(filterName);
+ // 1b.) try to write the changed filter to the configuration.
+ // This must result in an exception if the filter is mandatory
+ flushError = false;
+ try
+ {
+ xFlush.flush();
+ }
+ catch (WrappedTargetRuntimeException e)
+ {
+ flushError = true;
+ assertTrue("Unexpected exception while flushing removed filter '" + filterName + "'", isMandatory);
+ }
+ assertTrue("Expected exception was not thrown while flushing removed filter '" + filterName + "' Mandatory:" + isMandatory,
+ !(flushError ^ isMandatory));
+ }
+ String preMsg = "Could not find filter with state ";
+ String postMsg = " Please check if such filter is installed!";
+ assertTrue(preMsg + "'Mandatory=true'" + postMsg, mandatoryTrue);
+ assertTrue(preMsg + "'Mandatory=false'" + postMsg, mandatoryFalse);
+ assertTrue(preMsg + "'Finalized=true'" + postMsg, finalizedTrue);
+ assertTrue(preMsg + "'Finalized=false'" + postMsg, finalizedFalse);
+ }
+
+ /**
+ * print all properties with its values to <CODE>logger</CODE>. For debug purposes.
+ * @see stats.SimpleLogWriter
+ * @see com.sun.star.beans.PropertyValue
+ * @param props Sequence of PropertyValue
+ */
+ protected void printPropertyValues(PropertyValue[] props)
+ {
+ int i = 0;
+ while (i < props.length)
+ {
+ System.out.println(props[i].Name + ":" + props[i].Value.toString());
+ i++;
+ }
+ if (i < props.length)
+ {
+ System.out.println(props[i].Name + ":" + props[i].Value.toString());
+ }
+ }
+
+ /**
+ * returns the value of the specified (<CODE>pName</CODE>) property from a sequence of <CODE>PropertyValue</CODE>
+ * @param props a sequence of <CODE>PropertyValue</CODE>
+ * @param pName the name of the property the value should be returned
+ * @return the value of the property
+ */
+ protected Object getPropertyValueValue(PropertyValue[] props, String pName)
+ {
+ int i = 0;
+ while (i < props.length && !props[i].Name.equals(pName))
+ {
+ i++;
+ }
+ return i < props.length ? props[i].Value : null;
+ }
+
+ /**
+ * set a value of the specified (<CODE>pName</CODE>) property inside a sequence of <CODE>PropertyValue</CODE>
+ * @param props sequence of <CODE>PropertyValue</CODE>
+ * @param pName name of the property which should be changed
+ * @param pValue the value the property should be assigned
+ */
+ protected void setPropertyValueValue(PropertyValue[] props, String pName, Object pValue)
+ {
+ int i = 0;
+ while (i < props.length && !props[i].Name.equals(pName))
+ {
+ i++;
+ }
+ props[i].Value = pValue;
+ }
+
+ private XMultiServiceFactory getMSF()
+ {
+ return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager());
+ }
+
+ // setup and close connections
+ @BeforeClass
+ public static void setUpConnection() throws Exception
+ {
+ System.out.println("setUpConnection()");
+ connection.setUp();
+ }
+
+ @AfterClass
+ public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println("tearDownConnection()");
+ connection.tearDown();
+ }
+ private static final OfficeConnection connection = new OfficeConnection();
+}
diff --git a/filter/qa/complex/filter/misc/TypeDetection6FileFormat.java b/filter/qa/complex/filter/misc/TypeDetection6FileFormat.java
new file mode 100644
index 000000000..009fd52cf
--- /dev/null
+++ b/filter/qa/complex/filter/misc/TypeDetection6FileFormat.java
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package complex.filter.misc;
+
+import com.sun.star.container.XNameAccess;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+
+import util.utils;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+import static org.junit.Assert.*;
+
+
+public class TypeDetection6FileFormat
+{
+
+ XMultiServiceFactory xMSF;
+
+ /** Create the environment for following tests.
+ * Use either a component loader from desktop or
+ * from frame
+ * @throws Exception Exception
+ */
+ @Before public void before() throws Exception
+ {
+ xMSF = getMSF();
+ assertNotNull("Could not get XMultiServiceFactory", xMSF);
+ }
+
+ /**
+ * call the function <CODE>checkFileFormatSupport</CODE> to test <CODE>com.sun.star.document.FilterFactory</CODE>
+ * @see com.sun.star.document.FilterFactory
+ */
+ @Test public void checkFilterFactory() throws Exception
+ {
+ checkFileFormatSupport("com.sun.star.document.FilterFactory");
+ }
+
+ /**
+ * call the function <CODE>checkFileFormatSupport</CODE> to test <CODE>com.sun.star.document.TypeDetection</CODE>
+ * @see com.sun.star.document.TypeDetection
+ */
+ @Test public void checkTypeDetection() throws Exception
+ {
+ checkFileFormatSupport("com.sun.star.document.TypeDetection");
+ }
+
+ /**
+ * test the given service <CODE>serviceName</CODE>.
+ * The serve was created and the filter 'TypeDetection6FileFormat' was searched
+ * @param serviceName the name of the service to test
+ */
+ private void checkFileFormatSupport(String serviceName) throws Exception
+ {
+ System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ System.out.println("testing service '" + serviceName + "'");
+
+ XInterface oObj = null;
+ oObj = getTestObject(serviceName);
+ System.out.println("ImplName: " + utils.getImplName(oObj));
+ XNameAccess xNA = UnoRuntime.queryInterface(XNameAccess.class, oObj);
+ String msg = "Could not find filter 'TypeDetection6FileFormat'!";
+ msg += "\nMaybe 'TypeDetection6FileFormat.xcu' is not registered.";
+ assertTrue(msg, xNA.hasByName("TypeDetection6FileFormat"));
+ }
+
+ /**
+ * Creates an instance for the given <CODE>serviceName</CODE>
+ * @param serviceName the name of the service which should be created
+ * @throws Exception was thrown if creation fails
+ * @return <CODE>XInterface</CODE> of service
+ */
+ public XInterface getTestObject(String serviceName) throws Exception
+ {
+
+ Object oInterface = xMSF.createInstance(serviceName);
+
+ if (oInterface == null)
+ {
+ fail("Service wasn't created");
+ throw new Exception("could not create service '" + serviceName + "'");
+ }
+ return (XInterface) oInterface;
+ }
+
+ private XMultiServiceFactory getMSF()
+ {
+ return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager());
+ }
+
+ // setup and close connections
+ @BeforeClass public static void setUpConnection() throws Exception {
+ System.out.println("setUpConnection()");
+ connection.setUp();
+ }
+
+ @AfterClass public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println("tearDownConnection()");
+ connection.tearDown();
+ }
+
+ private static final OfficeConnection connection = new OfficeConnection();
+
+}
diff --git a/filter/qa/complex/filter/misc/TypeDetection6FileFormat.xcu b/filter/qa/complex/filter/misc/TypeDetection6FileFormat.xcu
new file mode 100644
index 000000000..2206e6942
--- /dev/null
+++ b/filter/qa/complex/filter/misc/TypeDetection6FileFormat.xcu
@@ -0,0 +1,42 @@
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+-->
+<?xml version="1.0" encoding="UTF-8"?>
+<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="TypeDetection" oor:package="org.openoffice.Office">
+ <node oor:name="Types">
+ <node oor:name="TypeDetection6FileFormat" oor:op="replace">
+ <prop oor:name="UIName">
+ <value xml:lang="x-no-translate">TypeDetection 6 FileFormat</value>
+ <value xml:lang="en-US">TypeDetection 6 FileFormat</value>
+ </prop>
+ <prop oor:name="Data">
+ <value>1,,,,,,</value>
+ </prop>
+ </node>
+ </node>
+ <node oor:name="Filters">
+ <node oor:name="TypeDetection6FileFormat" oor:op="replace">
+ <prop oor:name="UIName">
+ <value xml:lang="x-no-translate">FilterFactory 6 FileFormat</value>
+ <value xml:lang="en-US">FilterFactory 6 FileFormat</value>
+ </prop>
+ <prop oor:name="Data">
+ <value>0,TypeDetection6FileFormat,,,,,</value>
+ </prop>
+ </node>
+ </node>
+</oor:component-data>
diff --git a/filter/qa/cppunit/data/xslt/copy.xslt b/filter/qa/cppunit/data/xslt/copy.xslt
new file mode 100644
index 000000000..d46172a41
--- /dev/null
+++ b/filter/qa/cppunit/data/xslt/copy.xslt
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/">
+ <xsl:copy-of select="/"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/filter/qa/cppunit/msfilter-test.cxx b/filter/qa/cppunit/msfilter-test.cxx
new file mode 100644
index 000000000..439aad88b
--- /dev/null
+++ b/filter/qa/cppunit/msfilter-test.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sal/types.h>
+#include <tools/color.hxx>
+#include <filter/msfilter/util.hxx>
+
+#include <unotest/bootstrapfixturebase.hxx>
+
+namespace {
+
+class MSFilterTest
+ : public test::BootstrapFixtureBase
+{
+public:
+ void testTransColToIco();
+
+ CPPUNIT_TEST_SUITE(MSFilterTest);
+ CPPUNIT_TEST(testTransColToIco);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void MSFilterTest::testTransColToIco()
+{
+ const sal_uInt32 aStdCol[] = {
+ 0xeeeeee, 0xffff99, 0xff6600, 0xff3333, 0xff00cc, 0xff33ff, 0x9900ff, 0x6666ff, 0x00ccff, 0x66ffff, 0x33ff99, 0x99ff66, 0xccff00,
+ 0xdddddd, 0xffff66, 0xffcc00, 0xff9999, 0xff66cc, 0xff99ff, 0xcc66ff, 0x9999ff, 0x9999ff, 0x99ffff, 0x66ff99, 0x99ff99, 0xccff66,
+ 0xcccccc, 0xffff00, 0xff9900, 0xff6666, 0xff3399, 0xff66ff, 0x9933ff, 0x3333ff, 0x3399ff, 0x00ffff, 0x00ff66, 0x66ff66, 0x99ff33,
+ 0xb2b2b2, 0xcc9900, 0xff3300, 0xff0000, 0xff0066, 0xff00ff, 0x6600ff, 0x0000ff, 0x0066ff, 0x00cccc, 0x00cc33, 0x00cc00, 0x66ff00,
+ 0x999999, 0x996600, 0xcc3300, 0xcc0000, 0xcc0066, 0xcc00cc, 0x6600cc, 0x0000cc, 0x0066cc, 0x009999, 0x009933, 0x009900, 0x66cc00,
+ 0x808080, 0x663300, 0x801900, 0x990000, 0x990066, 0x990099, 0x330099, 0x000099, 0x006699, 0x006666, 0x007826, 0x006600, 0x669900,
+ 0x666666, 0x333300, 0x461900, 0x330000, 0x330033, 0x660066, 0x000033, 0x000066, 0x000080, 0x003333, 0x00331a, 0x003300, 0x193300,
+ 0x333333, 0x666633, 0x661900, 0x663333, 0x660033, 0x663366, 0x330066, 0x333366, 0x003366, 0x336666, 0x006633, 0x336633, 0x336600 };
+
+ const sal_uInt16 aExpected[] = {
+ 8, 7, 6, 6, 5, 5, 5, 2, 3, 3, 10, 4, 7,
+ 16, 7, 7, 6, 5, 5, 5, 2, 2, 3, 4, 4, 7,
+ 16, 7, 7, 6, 12, 5, 12, 2, 10, 3, 4, 4, 14,
+ 16, 14, 6, 6, 6, 5, 2, 2, 2, 3, 4, 4, 4,
+ 15, 14, 6, 6, 12, 5, 12, 2, 10, 10, 11, 11, 14,
+ 15, 1, 13, 13, 12, 12, 9, 9, 10, 10, 11, 11, 14,
+ 15, 14, 13, 13, 12, 12, 9, 9, 9, 10, 10, 11, 11,
+ 1, 14, 13, 13, 1, 12, 1, 9, 1, 10, 1, 11, 1 };
+
+ for( size_t i = 0; i < SAL_N_ELEMENTS(aStdCol); ++i)
+ {
+ const OString sMessage = "Index of unmatched color: " + OString::number(i);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), aExpected[i],
+ static_cast<sal_uInt16>(msfilter::util::TransColToIco( Color(ColorTransparency, aStdCol[i]) )));
+ }
+
+ // tdf#92471
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), static_cast<sal_uInt16>(msfilter::util::TransColToIco( Color( 0x6666ff ))));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), static_cast<sal_uInt16>(msfilter::util::TransColToIco( Color( 0x6566ff ))));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), static_cast<sal_uInt16>(msfilter::util::TransColToIco( Color( 0x6665ff ))));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), static_cast<sal_uInt16>(msfilter::util::TransColToIco( Color( 0x6666fe ))));
+
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MSFilterTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/cppunit/priority-test.cxx b/filter/qa/cppunit/priority-test.cxx
new file mode 100644
index 000000000..18e63c318
--- /dev/null
+++ b/filter/qa/cppunit/priority-test.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Unit test to check that we get the right filters for the right extensions.
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sal/types.h>
+
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <unotest/bootstrapfixturebase.hxx>
+
+
+using namespace css;
+
+namespace {
+
+class PriorityFilterTest
+ : public test::BootstrapFixtureBase
+{
+public:
+ void testPriority();
+
+ CPPUNIT_TEST_SUITE(PriorityFilterTest);
+ CPPUNIT_TEST(testPriority);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void PriorityFilterTest::testPriority()
+{
+ uno::Reference<document::XTypeDetection> xDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE("No type detection component", xDetection.is());
+
+ static struct {
+ const char *pURL;
+ const char *pFormat;
+ } const aToCheck[] = {
+ { "file:///tmp/foo.xls", "calc_MS_Excel_97" }
+ // TODO: expand this to check more of these priorities
+ };
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aToCheck); i++)
+ {
+ OUString aURL = OUString::createFromAscii(aToCheck[i].pURL);
+ try
+ {
+ OUString aTypeName = xDetection->queryTypeByURL(aURL);
+
+ OUString aFormatCorrect = OUString::createFromAscii(aToCheck[i].pFormat);
+ OUString aMsg = "Mis-matching formats "
+ "'" +
+ aTypeName +
+ "' should be '" +
+ aFormatCorrect +
+ "'";
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(OUStringToOString(aMsg,
+ RTL_TEXTENCODING_UTF8).getStr(),
+ aFormatCorrect, aTypeName);
+ }
+ catch (const uno::Exception &e)
+ {
+ OUString aMsg = "Exception querying for type: '" + e.Message + "'";
+ CPPUNIT_FAIL(OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PriorityFilterTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/cppunit/xslt-test.cxx b/filter/qa/cppunit/xslt-test.cxx
new file mode 100644
index 000000000..4e0c621d6
--- /dev/null
+++ b/filter/qa/cppunit/xslt-test.cxx
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <condition_variable>
+#include <mutex>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <rtl/ref.hxx>
+
+#include <osl/file.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/io/XStreamListener.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/xml/xslt/XSLTTransformer.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <test/bootstrapfixture.hxx>
+
+
+using namespace ::com::sun::star;
+
+
+namespace {
+
+class XsltFilterTest
+ : public test::BootstrapFixture
+{
+public:
+ void testXsltCopyOld();
+ void testXsltCopyNew();
+
+ CPPUNIT_TEST_SUITE(XsltFilterTest);
+ CPPUNIT_TEST(testXsltCopyOld);
+ CPPUNIT_TEST(testXsltCopyNew);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+class Listener : public ::cppu::WeakImplHelper<io::XStreamListener>
+{
+public:
+ Listener() : m_bDone(false) {}
+
+ void wait() {
+ std::unique_lock<std::mutex> g(m_mutex);
+ m_cond.wait(g, [this]() { return m_bDone; });
+ }
+
+private:
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+ bool m_bDone;
+
+ virtual void SAL_CALL disposing(const lang::EventObject&) noexcept override {}
+ virtual void SAL_CALL started() noexcept override {}
+ virtual void SAL_CALL closed() noexcept override { notifyDone(); }
+ virtual void SAL_CALL terminated() noexcept override { notifyDone(); }
+ virtual void SAL_CALL error(const uno::Any& e) override
+ {
+ notifyDone(); // set on error too, otherwise main thread waits forever
+ SAL_WARN("filter.xslt", e);
+ CPPUNIT_FAIL("exception while in XSLT");
+ }
+
+ void notifyDone() {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ m_bDone = true;
+ m_cond.notify_all();
+ }
+};
+
+void XsltFilterTest::testXsltCopyNew()
+{
+ OUString tempDirURL;
+ osl_getTempDirURL(&tempDirURL.pData);
+ oslFileHandle tempFile;
+ OUString tempURL;
+ osl::File::RC rc = osl::File::createTempFile(nullptr, &tempFile, &tempURL);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, rc);
+ osl_closeFile(tempFile); // close it so xSFA can open it on WNT
+
+ OUString source(
+ m_directories.getURLFromSrc(u"/filter/source/xsltfilter/xsltfilter.component"));
+ uno::Sequence<uno::Any> args{
+ uno::Any(beans::NamedValue("StylesheetURL",
+ uno::Any(m_directories.getURLFromSrc(u"/filter/qa/cppunit/data/xslt/copy.xslt")))),
+ uno::Any(beans::NamedValue("SourceURL", uno::Any(source))),
+ uno::Any(beans::NamedValue("TargetURL", uno::Any(tempURL))),
+ uno::Any(beans::NamedValue("SourceBaseURL",
+ uno::Any(m_directories.getURLFromSrc(u"/filter/source/xsltfilter/")))),
+ uno::Any(beans::NamedValue("TargetBaseURL", uno::Any(tempDirURL))),
+ uno::Any(beans::NamedValue("SystemType", uno::Any(OUString()))),
+ uno::Any(beans::NamedValue("PublicType", uno::Any(OUString())))
+ };
+
+ uno::Reference<ucb::XSimpleFileAccess3> xSFA =
+ ucb::SimpleFileAccess::create(getComponentContext());
+
+ uno::Reference<io::XInputStream> xIn = xSFA->openFileRead(source);
+ uno::Reference<io::XOutputStream> xOut = xSFA->openFileWrite(tempURL);
+
+ rtl::Reference<Listener> xListener = new Listener();
+
+ uno::Reference<xml::xslt::XXSLTTransformer> xXslt(
+ xml::xslt::XSLTTransformer::create(getComponentContext(), args));
+
+ xXslt->addListener(xListener);
+ xXslt->setInputStream(xIn);
+ xXslt->setOutputStream(xOut);
+
+ xXslt->start();
+
+ xListener->wait();
+
+ xIn->closeInput();
+ xOut->closeOutput();
+
+ osl::File foo(tempURL); // apparently it's necessary to open it again?
+ foo.open(osl_File_OpenFlag_Read);
+ sal_uInt64 size(0);
+ foo.getSize(size);
+ CPPUNIT_ASSERT(size > 1000); // check that something happened
+ foo.close();
+ osl_removeFile(tempURL.pData);
+}
+
+void XsltFilterTest::testXsltCopyOld()
+{
+ OUString tempDirURL;
+ osl_getTempDirURL(&tempDirURL.pData);
+ oslFileHandle tempFile;
+ OUString tempURL;
+ osl::File::RC rc = osl::File::createTempFile(nullptr, &tempFile, &tempURL);
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, rc);
+ osl_closeFile(tempFile); // close it so xSFA can open it on WNT
+
+ OUString source(
+ m_directories.getURLFromSrc(u"/filter/source/xsltfilter/xsltfilter.component"));
+ uno::Sequence<uno::Any> args{
+ uno::Any(beans::NamedValue("StylesheetURL",
+ uno::Any(m_directories.getURLFromSrc(u"/filter/qa/cppunit/data/xslt/copy.xslt")))),
+ uno::Any(beans::NamedValue("SourceURL", uno::Any(source))),
+ uno::Any(beans::NamedValue("TargetURL", uno::Any(tempURL))),
+ uno::Any(beans::NamedValue("SourceBaseURL",
+ uno::Any(m_directories.getURLFromSrc(u"/filter/source/xsltfilter/")))),
+ uno::Any(beans::NamedValue("TargetBaseURL", uno::Any(tempDirURL))),
+ uno::Any(beans::NamedValue("SystemType", uno::Any(OUString()))),
+ uno::Any(beans::NamedValue("PublicType", uno::Any(OUString())))
+ };
+
+ uno::Reference<ucb::XSimpleFileAccess3> xSFA =
+ ucb::SimpleFileAccess::create(getComponentContext());
+
+ uno::Reference<io::XInputStream> xIn = xSFA->openFileRead(source);
+ uno::Reference<io::XOutputStream> xOut = xSFA->openFileWrite(tempURL);
+
+ rtl::Reference<Listener> xListener = new Listener();
+
+ uno::Reference<xml::xslt::XXSLTTransformer> xXslt(
+ getMultiServiceFactory()->createInstance(
+ "com.sun.star.comp.documentconversion.LibXSLTTransformer"),
+ uno::UNO_QUERY_THROW);
+
+ uno::Reference<lang::XInitialization> xInit(xXslt, uno::UNO_QUERY_THROW);
+ xInit->initialize(args);
+ xXslt->addListener(xListener);
+ xXslt->setInputStream(xIn);
+ xXslt->setOutputStream(xOut);
+
+ xXslt->start();
+
+ xListener->wait();
+
+ xIn->closeInput();
+ xOut->closeOutput();
+
+ osl::File foo(tempURL); // apparently it's necessary to open it again?
+ foo.open(osl_File_OpenFlag_Read);
+ sal_uInt64 size(0);
+ foo.getSize(size);
+ CPPUNIT_ASSERT(size > 1000); // check that something happened
+ foo.close();
+ osl_removeFile(tempURL.pData);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XsltFilterTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/data/picture.pdf b/filter/qa/data/picture.pdf
new file mode 100644
index 000000000..79af2b349
--- /dev/null
+++ b/filter/qa/data/picture.pdf
Binary files differ
diff --git a/filter/qa/pdf.cxx b/filter/qa/pdf.cxx
new file mode 100644
index 000000000..ec5460b9f
--- /dev/null
+++ b/filter/qa/pdf.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <tools/stream.hxx>
+#include <unotools/streamwrap.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <tools/helpers.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Covers filter/source/pdf/ fixes.
+class Test : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+void Test::setUp()
+{
+ test::BootstrapFixture::setUp();
+ MacrosTest::setUpNssGpg(m_directories, "filter_pdf");
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void Test::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/filter/qa/data/";
+
+CPPUNIT_TEST_FIXTURE(Test, testSignCertificateSubjectName)
+{
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
+ = xml::crypto::SEInitializer::create(mxComponentContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
+ = xSEInitializer->createSecurityContext(OUString());
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
+ = xSecurityContext->getSecurityEnvironment();
+ uno::Sequence<beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("SignPDF", true),
+ comphelper::makePropertyValue(
+ "SignCertificateSubjectName",
+ OUString(
+ "CN=Xmlsecurity RSA Test example Alice,O=Xmlsecurity RSA Test,ST=England,C=UK")),
+ };
+ if (!GetValidCertificate(xSecurityEnvironment->getPersonalCertificates(), aFilterData))
+ {
+ return;
+ }
+
+ // Given an empty document:
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When exporting to PDF, and referring to a certificate using a subject name:
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
+ uno::Reference<document::XFilter> xFilter(
+ xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(getComponent());
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+
+ uno::Sequence<beans::PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")),
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xOutputStream),
+ };
+ xFilter->filter(aDescriptor);
+
+ // Then make sure the resulting PDF has a signature:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ // Without the accompanying fix in place, this test would have failed, as signing was enabled
+ // without configuring a certificate, so the whole export failed.
+ CPPUNIT_ASSERT(pPdfDocument);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testPdfDecompositionSize)
+{
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+
+ // Given an empty Writer document:
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When inserting a 267 points wide PDF image into the document:
+ uno::Sequence<beans::PropertyValue> aArgs = {
+ comphelper::makePropertyValue("FileName",
+ m_directories.getURLFromSrc(DATA_DIRECTORY) + "picture.pdf"),
+ };
+ dispatchCommand(getComponent(), ".uno:InsertGraphic", aArgs);
+
+ // Then make sure that its size is correct:
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xDrawPage = xDrawPageSupplier->getDrawPage();
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ auto xGraphic = xShape->getPropertyValue("Graphic").get<uno::Reference<graphic::XGraphic>>();
+ CPPUNIT_ASSERT(xGraphic.is());
+ Graphic aGraphic(xGraphic);
+ basegfx::B2DRange aRange = aGraphic.getVectorGraphicData()->getRange();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 9437
+ // - Actual : 34176
+ // i.e. the width was too large, it used all width of the body frame.
+ // 9437 mm100 is 267.507 points from the file.
+#if defined MACOSX
+ // TODO the bitmap size is larger (75486) on macOS, but that should not affect the logic size.
+ (void)aRange;
+#else
+ // Unfortunately, this test is DPI-dependent.
+ // Use some allowance (~1/2 pt) to let it pass on non-default DPI.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(9437, aRange.getWidth(), 20.0);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testWatermarkColor)
+{
+ // Given an empty Writer document:
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When exporting that as PDF with a red watermark:
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
+ uno::Reference<document::XFilter> xFilter(
+ xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(getComponent());
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+ uno::Sequence<beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Watermark", OUString("X")),
+ comphelper::makePropertyValue("WatermarkColor", static_cast<sal_Int32>(0xff0000)),
+ };
+ uno::Sequence<beans::PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")),
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xOutputStream),
+ };
+ xFilter->filter(aDescriptor);
+
+ // Then make sure that the watermark color is correct:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ CPPUNIT_ASSERT(pPdfDocument);
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(0);
+ Color aFillColor = pFormObject->getFillColor();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: rgba[ff0000ff]
+ // - Actual : rgba[00ff00ff]
+ // i.e. the color was the (default) green, not red.
+ CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xff0000), aFillColor);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testWatermarkFontHeight)
+{
+ // Given an empty Writer document:
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When exporting that as PDF with a 100pt-sized watermark:
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
+ uno::Reference<document::XFilter> xFilter(
+ xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(getComponent());
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+ sal_Int32 nExpectedFontSize = 100;
+ uno::Sequence<beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Watermark", OUString("X")),
+ comphelper::makePropertyValue("WatermarkFontHeight", nExpectedFontSize),
+ };
+ uno::Sequence<beans::PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")),
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xOutputStream),
+ };
+ xFilter->filter(aDescriptor);
+
+ // Then make sure that the watermark font size is correct:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ CPPUNIT_ASSERT(pPdfDocument);
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(0);
+ sal_Int32 nFontSize = pFormObject->getFontSize();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 100
+ // - Actual : 594
+ // i.e. the font size was automatic, could not specify an explicit size.
+ CPPUNIT_ASSERT_EQUAL(nExpectedFontSize, nFontSize);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testWatermarkFontName)
+{
+ // Given an empty Writer document:
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When exporting that as PDF with a serif watermark:
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
+ uno::Reference<document::XFilter> xFilter(
+ xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(getComponent());
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+ OUString aExpectedFontName("Liberation Serif");
+ uno::Sequence<beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Watermark", OUString("X")),
+ comphelper::makePropertyValue("WatermarkFontName", aExpectedFontName),
+ };
+ uno::Sequence<beans::PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")),
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xOutputStream),
+ };
+ xFilter->filter(aDescriptor);
+
+ // Then make sure that the watermark font name is correct:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ CPPUNIT_ASSERT(pPdfDocument);
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(0);
+ OUString aFontName = pFormObject->getFontName();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: Liberation Serif
+ // - Actual : Helvetica
+ // i.e. the font name was sans, could not specify an explicit name.
+ CPPUNIT_ASSERT_EQUAL(aExpectedFontName, aFontName);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testWatermarkRotateAngle)
+{
+ // Given an empty Writer document:
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPDFium)
+ return;
+ getComponent().set(
+ loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"));
+
+ // When exporting that as PDF with a rotated watermark:
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
+ uno::Reference<document::XFilter> xFilter(
+ xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(getComponent());
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+ // 45.0 degrees, counter-clockwise.
+ sal_Int32 nExpectedRotateAngle = 45;
+ uno::Sequence<beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Watermark", OUString("X")),
+ comphelper::makePropertyValue("WatermarkRotateAngle", nExpectedRotateAngle * 10),
+ };
+ uno::Sequence<beans::PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")),
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xOutputStream),
+ };
+ xFilter->filter(aDescriptor);
+
+ // Then make sure that the watermark rotation angle is correct:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ CPPUNIT_ASSERT(pPdfDocument);
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(0);
+ basegfx::B2DHomMatrix aMatrix = pFormObject->getMatrix();
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate{};
+ double fShearX{};
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ sal_Int32 nActualRotateAngle = NormAngle360(basegfx::rad2deg<1>(fRotate));
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 45
+ // - Actual : 270
+ // i.e. the rotation angle was 270 for an A4 page, not the requested 45 degrees.
+ CPPUNIT_ASSERT_EQUAL(nExpectedRotateAngle, nActualRotateAngle);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/unit/data/TransparentText.odg b/filter/qa/unit/data/TransparentText.odg
new file mode 100644
index 000000000..d3027d17d
--- /dev/null
+++ b/filter/qa/unit/data/TransparentText.odg
Binary files differ
diff --git a/filter/qa/unit/data/attributeRedefinedTest.odp b/filter/qa/unit/data/attributeRedefinedTest.odp
new file mode 100644
index 000000000..dfb814bfb
--- /dev/null
+++ b/filter/qa/unit/data/attributeRedefinedTest.odp
Binary files differ
diff --git a/filter/qa/unit/data/calc.ots b/filter/qa/unit/data/calc.ots
new file mode 100644
index 000000000..d16d2307f
--- /dev/null
+++ b/filter/qa/unit/data/calc.ots
Binary files differ
diff --git a/filter/qa/unit/data/custom-bullet.fodp b/filter/qa/unit/data/custom-bullet.fodp
new file mode 100644
index 000000000..4139260f9
--- /dev/null
+++ b/filter/qa/unit/data/custom-bullet.fodp
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.presentation">
+ <office:automatic-styles>
+ <style:page-layout style:name="PM1">
+ <style:page-layout-properties fo:margin-top="0cm" fo:margin-bottom="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:page-width="28cm" fo:page-height="15.75cm" style:print-orientation="landscape"/>
+ </style:page-layout>
+ <style:style style:name="gr1" style:family="graphic" style:parent-style-name="standard">
+ <style:graphic-properties draw:stroke="none" svg:stroke-color="#000000" draw:fill="none" draw:fill-color="#ffffff" draw:auto-grow-height="true" draw:auto-grow-width="false" fo:max-height="0cm" fo:min-height="0cm"/>
+ </style:style>
+ <text:list-style style:name="L1">
+ <text:list-level-style-bullet text:level="1" text:bullet-char="-">
+ <style:list-level-properties text:min-label-width="0.6cm"/>
+ <style:text-properties fo:font-family="OpenSymbol" style:font-style-name="Regular" style:font-charset="x-symbol" style:use-window-font-color="true" fo:font-size="45%"/>
+ </text:list-level-style-bullet>
+ </text:list-style>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Default" style:page-layout-name="PM1">
+ </style:master-page>
+ </office:master-styles>
+ <office:body>
+ <office:presentation>
+ <draw:page draw:name="page1" draw:master-page-name="Default">
+ <draw:frame draw:style-name="gr1" draw:text-style-name="P8" draw:layer="layout" svg:width="9.525cm" svg:height="0.962cm" svg:x="3.175cm" svg:y="2.54cm">
+ <draw:text-box>
+ <text:list text:style-name="L1">
+ <text:list-item>
+ <text:p>hello</text:p>
+ </text:list-item>
+ </text:list>
+ </draw:text-box>
+ </draw:frame>
+ </draw:page>
+ </office:presentation>
+ </office:body>
+</office:document>
diff --git a/filter/qa/unit/data/empty.doc b/filter/qa/unit/data/empty.doc
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/filter/qa/unit/data/empty.doc
diff --git a/filter/qa/unit/data/empty.odp b/filter/qa/unit/data/empty.odp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/filter/qa/unit/data/empty.odp
diff --git a/filter/qa/unit/data/empty.ods b/filter/qa/unit/data/empty.ods
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/filter/qa/unit/data/empty.ods
diff --git a/filter/qa/unit/data/empty.odt b/filter/qa/unit/data/empty.odt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/filter/qa/unit/data/empty.odt
diff --git a/filter/qa/unit/data/empty.pptx b/filter/qa/unit/data/empty.pptx
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/filter/qa/unit/data/empty.pptx
diff --git a/filter/qa/unit/data/filter-dialogs-test.txt b/filter/qa/unit/data/filter-dialogs-test.txt
new file mode 100644
index 000000000..22792ad28
--- /dev/null
+++ b/filter/qa/unit/data/filter-dialogs-test.txt
@@ -0,0 +1,49 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# This file contains all dialogs that the unit tests in the module
+# will work on if it is in script mode. It will read one-by-one,
+# try to open it and create a screenshot that will be saved in
+# workdir/screenshots using the pattern of the ui-file name.
+#
+# Syntax:
+# - empty lines are allowed
+# - lines starting with '#' are treated as comment
+# - all other lines should contain a *.ui filename in the same
+# notation as in the dialog constructors (see code)
+
+#
+# The 'known' dialogs which have a hard-coded representation
+# in registerKnownDialogsByID/createDialogByID
+#
+
+# No known dialogs in filter for now
+
+#
+# Dialogs without a hard-coded representation. These will
+# be visualized using a fallback based on weld::Builder
+#
+
+# currently deactivated, leads to problems and the test to not work
+# This is typically a hint that these should be hard-coded in the
+# test case since they need some document and model data to work
+
+filter/ui/pdfoptionsdialog.ui
+filter/ui/xsltfilterdialog.ui
+filter/ui/pdfgeneralpage.ui
+filter/ui/pdfviewpage.ui
+filter/ui/pdfuserinterfacepage.ui
+filter/ui/pdfsecuritypage.ui
+filter/ui/pdflinkspage.ui
+filter/ui/pdfsignpage.ui
+filter/ui/xmlfiltertabpagegeneral.ui
+filter/ui/xmlfiltertabpagetransformation.ui
+filter/ui/testxmlfilter.ui
+filter/ui/warnpdfdialog.ui
+filter/ui/xmlfiltersettings.ui
diff --git a/filter/qa/unit/data/impress.otp b/filter/qa/unit/data/impress.otp
new file mode 100644
index 000000000..199a5f9d4
--- /dev/null
+++ b/filter/qa/unit/data/impress.otp
Binary files differ
diff --git a/filter/qa/unit/data/preserve-jpg.odt b/filter/qa/unit/data/preserve-jpg.odt
new file mode 100644
index 000000000..83768bd47
--- /dev/null
+++ b/filter/qa/unit/data/preserve-jpg.odt
Binary files differ
diff --git a/filter/qa/unit/data/semi-transparent-fill.odg b/filter/qa/unit/data/semi-transparent-fill.odg
new file mode 100644
index 000000000..713f48991
--- /dev/null
+++ b/filter/qa/unit/data/semi-transparent-fill.odg
Binary files differ
diff --git a/filter/qa/unit/data/semi-transparent-line.odg b/filter/qa/unit/data/semi-transparent-line.odg
new file mode 100644
index 000000000..2d28a694c
--- /dev/null
+++ b/filter/qa/unit/data/semi-transparent-line.odg
Binary files differ
diff --git a/filter/qa/unit/data/shape-nographic.odp b/filter/qa/unit/data/shape-nographic.odp
new file mode 100644
index 000000000..43186d614
--- /dev/null
+++ b/filter/qa/unit/data/shape-nographic.odp
Binary files differ
diff --git a/filter/qa/unit/data/tdf114428.xhtml b/filter/qa/unit/data/tdf114428.xhtml
new file mode 100644
index 000000000..f08f0fa4a
--- /dev/null
+++ b/filter/qa/unit/data/tdf114428.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Title of document</title>
+ </head>
+ <body>hello world</body>
+</html>
diff --git a/filter/qa/unit/data/writer.ott b/filter/qa/unit/data/writer.ott
new file mode 100644
index 000000000..1ded03150
--- /dev/null
+++ b/filter/qa/unit/data/writer.ott
Binary files differ
diff --git a/filter/qa/unit/filter-dialogs-test.cxx b/filter/qa/unit/filter-dialogs-test.cxx
new file mode 100644
index 000000000..946a1961d
--- /dev/null
+++ b/filter/qa/unit/filter-dialogs-test.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+#include <test/screenshot_test.hxx>
+#include <vcl/abstdlg.hxx>
+
+using namespace ::com::sun::star;
+
+/// Test opening a dialog in filter
+class FilterDialogsTest : public ScreenshotTest
+{
+private:
+ /// helper method to populate KnownDialogs, called in setUp(). Needs to be
+ /// written and has to add entries to KnownDialogs
+ virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override;
+
+ /// dialog creation for known dialogs by ID. Has to be implemented for
+ /// each registered known dialog
+ virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override;
+
+public:
+ FilterDialogsTest();
+
+ // try to open a dialog
+ void openAnyDialog();
+
+ CPPUNIT_TEST_SUITE(FilterDialogsTest);
+ CPPUNIT_TEST(openAnyDialog);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+FilterDialogsTest::FilterDialogsTest() {}
+
+void FilterDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/)
+{
+ // fill map of known dialogs
+}
+
+VclPtr<VclAbstractDialog> FilterDialogsTest::createDialogByID(sal_uInt32 /*nID*/)
+{
+ return nullptr;
+}
+
+void FilterDialogsTest::openAnyDialog()
+{
+ /// process input file containing the UXMLDescriptions of the dialogs to dump
+ processDialogBatchFile(u"filter/qa/unit/data/filter-dialogs-test.txt");
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FilterDialogsTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
new file mode 100644
index 000000000..22695acd6
--- /dev/null
+++ b/filter/qa/unit/svg.cxx
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <unotools/streamwrap.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <tools/stream.hxx>
+
+using namespace ::com::sun::star;
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/filter/qa/unit/data/";
+
+/// SVG filter tests.
+class SvgFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+ void load(std::u16string_view rURL);
+};
+
+void SvgFilterTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void SvgFilterTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void SvgFilterTest::load(std::u16string_view rFileName)
+{
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName;
+ mxComponent = loadFromDesktop(aURL);
+}
+
+void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
+{
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg"));
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg)
+{
+#if !defined(MACOSX)
+ // Load a document with a jpeg image in it.
+ load(u"preserve-jpg.odt");
+
+ // Select the image.
+ dispatchCommand(getComponent(), ".uno:JumpToNextFrame", {});
+
+ // Export the selection to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
+ aMediaDescriptor["SelectionOnly"] <<= true;
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Make sure that the original JPG data is reused and we don't perform a PNG re-compress.
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ OUString aAttributeValue = getXPath(pXmlDoc, "//svg:image", "href");
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expression: aAttributeValue.startsWith("data:image/jpeg")
+ // i.e. the SVG export result re-compressed the image as PNG, even if the original and the
+ // transformed image is the same, so there is no need for that.
+ CPPUNIT_ASSERT(aAttributeValue.startsWith("data:image/jpeg"));
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentLine)
+{
+ // Load a document with a semi-transparent line shape.
+ load(u"semi-transparent-line.odg");
+
+ // Export it to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Get the style of the group around the actual <path> element.
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ OUString aStyle = getXPath(
+ pXmlDoc, "//svg:g[@class='com.sun.star.drawing.LineShape']/svg:g/svg:g", "style");
+ // Without the accompanying fix in place, this test would have failed, as the style was
+ // "mask:url(#mask1)", not "opacity: <value>".
+ CPPUNIT_ASSERT(aStyle.startsWith("opacity: ", &aStyle));
+ int nPercent = std::round(aStyle.toDouble() * 100);
+ // Make sure that the line is still 30% opaque, rather than completely invisible.
+ CPPUNIT_ASSERT_EQUAL(30, nPercent);
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentFillWithTransparentLine)
+{
+ // Load a document with a shape with semi-transparent fill and line
+ load(u"semi-transparent-fill.odg");
+
+ // Export it to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Get the style of the group around the actual <path> element.
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ OUString aStyle = getXPath(
+ pXmlDoc, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:g", "style");
+ CPPUNIT_ASSERT(aStyle.startsWith("opacity: ", &aStyle));
+ int nPercent = std::round(aStyle.toDouble() * 100);
+ // Make sure that the line is still 50% opaque
+ CPPUNIT_ASSERT_EQUAL(50, nPercent);
+
+ // Get the stroke of the fill of the EllipseShape (it must be "none")
+ OUString aStroke = getXPath(
+ pXmlDoc, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:path", "stroke");
+ // Without the accompanying fix in place, this test would have failed, as the stroke was
+ // "rgb(255,255,255)", not "none".
+ CPPUNIT_ASSERT_EQUAL(OUString("none"), aStroke);
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText)
+{
+ // Two shapes, one with transparent text and the other one with
+ // opaque text. We expect both to be exported to the SVG with the
+ // correct transparency factor applied for the first shape.
+
+ // Load draw document with transparent text in one box
+ load(u"TransparentText.odg");
+
+ // Export to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+
+ // We expect 2 groups of class "TextShape" that
+ // have some svg:text node inside.
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 1
+ // i.e. the 2nd shape lots its text.
+
+ assertXPath(pXmlDoc, "//svg:g[@class='TextShape']//svg:text", 2);
+
+ // First shape has semi-transparent text.
+ assertXPath(pXmlDoc, "//svg:text[1]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity='0.8']");
+
+ // Second shape has normal text.
+ assertXPath(pXmlDoc, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentMultiParaText)
+{
+ // Given a shape with semi-transparent, multi-paragraph text:
+ getComponent()
+ = loadFromDesktop("private:factory/simpress", "com.sun.star.drawing.DrawingDocument");
+ uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.TextShape"), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XShapes> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ xDrawPage->add(xShape);
+ xShape->setSize(awt::Size(10000, 10000));
+ uno::Reference<text::XSimpleText> xShapeText(xShape, uno::UNO_QUERY);
+ uno::Reference<text::XTextCursor> xCursor = xShapeText->createTextCursor();
+ xShapeText->insertString(xCursor, "foo", /*bAbsorb=*/false);
+ xShapeText->insertControlCharacter(xCursor, text::ControlCharacter::APPEND_PARAGRAPH,
+ /*bAbsorb=*/false);
+ xShapeText->insertString(xCursor, "bar", /*bAbsorb=*/false);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ xShapeProps->setPropertyValue("CharColor", uno::Any(static_cast<sal_Int32>(0xff0000)));
+ xShapeProps->setPropertyValue("CharTransparence", uno::Any(static_cast<sal_Int16>(20)));
+
+ // When exporting to SVG:
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Then make sure that the two semi-tranparent paragraphs have the same X position:
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]", "x",
+ "250");
+ assertXPath(pXmlDoc,
+ "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]/svg:tspan",
+ "fill-opacity", "0.8");
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 250
+ // - Actual : 8819
+ // i.e. the X position of the second paragraph was wrong.
+ assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]", "x",
+ "250");
+ assertXPath(pXmlDoc,
+ "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]/svg:tspan",
+ "fill-opacity", "0.8");
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testShapeNographic)
+{
+ // Load a document containing a 3D shape.
+ load(u"shape-nographic.odp");
+
+ // Export to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // An uncaught exception of type com.sun.star.io.IOException
+ // - SfxBaseModel::impl_store <private:stream> failed: 0xc10(Error Area:Io Class:Write Code:16)
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testCustomBullet)
+{
+ // Given a presentation with a custom bullet:
+ load(u"custom-bullet.fodp");
+
+ // When exporting that to SVG:
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Then make sure the bullet glyph is not lost:
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // - XPath '//svg:g[@class='BulletChars']//svg:path' number of nodes is incorrect
+ // i.e. the custom bullet used '<use transform="scale(285,285)"
+ // xlink:href="#bullet-char-template-45"/>', but nobody produced a bullet-char-template-45,
+ // instead we need the path of the glyph inline.
+ CPPUNIT_ASSERT(!getXPath(pXmlDoc, "//svg:g[@class='BulletChars']//svg:path", "d").isEmpty());
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, attributeRedefinedTest)
+{
+ // Load document containing empty paragraphs with ids.
+ load(u"attributeRedefinedTest.odp");
+
+ // Export to SVG.
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+
+ // We expect four paragraph
+ // 2 empty paragraphs with ids
+ // 2 paragraphs with text
+ // Without the accompanying fix the test would have failed with
+ // Expected : 4
+ // Actual : 2
+ // i.e. 2 of the empty paragraph do not get generated even if there
+ // is id imported for the paragraphs
+ // If we don't create the empty paragraphs the id attribute gets redefined like this:
+ // <tspan id="id14" id="id15" id="id17" class="TextParagraph" font-family="Bahnschrift Light" font-size="1129px" font-weight="400">
+
+ OString xPath = "//svg:g[@class='TextShape']//svg:text[@class='SVGTextShape']//"
+ "svg:tspan[@class='TextParagraph']";
+ assertXPath(pXmlDoc, xPath, 4);
+
+ //assert that each tspan element with TextParagraph class has id and the tspan element of
+ //each empty paragraph does not contain tspan element with class TextPosition
+ assertXPath(pXmlDoc, xPath + "[1]", "id", "id4");
+ assertXPath(pXmlDoc, xPath + "[2]", "id", "id5");
+ assertXPath(pXmlDoc, xPath + "[2]//svg:tspan[@class='TextPosition']", 0);
+ assertXPath(pXmlDoc, xPath + "[3]", "id", "id6");
+ assertXPath(pXmlDoc, xPath + "[3]//svg:tspan[@class='TextPosition']", 0);
+ assertXPath(pXmlDoc, xPath + "[4]", "id", "id7");
+}
+
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testTab)
+{
+ // Given a shape with "A\tB" text:
+ getComponent() = loadFromDesktop("private:factory/simpress",
+ "com.sun.star.presentation.PresentationDocument");
+ uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.TextShape"), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XShapes> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ xDrawPage->add(xShape);
+ xShape->setSize(awt::Size(10000, 10000));
+ uno::Reference<text::XTextRange> xShapeText(xShape, uno::UNO_QUERY);
+ xShapeText->setString("A\tB");
+
+ // When exporting that document to SVG:
+ uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+ aMediaDescriptor["OutputStream"] <<= xOut;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Then make sure the the tab is not lost:
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 1
+ // i.e. the 2nd text portion was not positioned, which looked as if the tab is lost.
+ assertXPath(pXmlDoc, "//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition']", 2);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/unit/textfilterdetect.cxx b/filter/qa/unit/textfilterdetect.cxx
new file mode 100644
index 000000000..3c8daf2f2
--- /dev/null
+++ b/filter/qa/unit/textfilterdetect.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/sheet/XCellRangesAccess.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <sfx2/docfac.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/streamwrap.hxx>
+#include <tools/stream.hxx>
+
+namespace com::sun::star::io
+{
+class XInputStream;
+}
+
+using namespace com::sun::star;
+
+namespace
+{
+/// Test class for PlainTextFilterDetect.
+class TextFilterDetectTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+public:
+ void setUp() override;
+};
+
+void TextFilterDetectTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/filter/qa/unit/data/";
+
+CPPUNIT_TEST_FIXTURE(TextFilterDetectTest, testTdf114428)
+{
+ uno::Reference<document::XExtendedFilterDetection> xDetect(
+ getMultiServiceFactory()->createInstance("com.sun.star.comp.filters.PlainTextFilterDetect"),
+ uno::UNO_QUERY);
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf114428.xhtml";
+ SvFileStream aStream(aURL, StreamMode::READ);
+ uno::Reference<io::XInputStream> xStream(new utl::OStreamWrapper(aStream));
+ uno::Sequence<beans::PropertyValue> aDescriptor
+ = { comphelper::makePropertyValue("DocumentService",
+ OUString("com.sun.star.text.TextDocument")),
+ comphelper::makePropertyValue("InputStream", xStream),
+ comphelper::makePropertyValue("TypeName", OUString("generic_HTML")) };
+ xDetect->detect(aDescriptor);
+ utl::MediaDescriptor aMediaDesc(aDescriptor);
+ OUString aFilterName = aMediaDesc.getUnpackedValueOrDefault("FilterName", OUString());
+ // This was empty, XML declaration caused HTML detect to not handle XHTML.
+ CPPUNIT_ASSERT_EQUAL(OUString("HTML (StarWriter)"), aFilterName);
+}
+
+CPPUNIT_TEST_FIXTURE(TextFilterDetectTest, testEmptyFile)
+{
+ const OUString sDataDirectory = m_directories.getURLFromSrc(DATA_DIRECTORY);
+ auto supportsService = [](const uno::Reference<lang::XComponent>& x, const OUString& s) {
+ return uno::Reference<lang::XServiceInfo>(x, uno::UNO_QUERY_THROW)->supportsService(s);
+ };
+
+ // Given an empty file, with a pptx extension
+ // When loading the file
+ auto xComponent = loadFromDesktop(sDataDirectory + "empty.pptx");
+
+ // Then make sure it is opened in Impress.
+ // Without the accompanying fix in place, this test would have failed, as it was opened in
+ // Writer instead.
+ CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.presentation.PresentationDocument"));
+ xComponent->dispose();
+
+ // Now also test ODT
+ xComponent = loadFromDesktop(sDataDirectory + "empty.odt");
+ // Make sure it opens in Writer.
+ CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.text.TextDocument"));
+ xComponent->dispose();
+
+ // ... and ODS
+ xComponent = loadFromDesktop(sDataDirectory + "empty.ods");
+ // Make sure it opens in Calc.
+ CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.sheet.SpreadsheetDocument"));
+ xComponent->dispose();
+
+ // ... and ODP
+ xComponent = loadFromDesktop(sDataDirectory + "empty.odp");
+ // Without the accompanying fix in place, this test would have failed, as it was opened in
+ // Writer instead.
+ CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.presentation.PresentationDocument"));
+ xComponent->dispose();
+
+ // ... and DOC
+ // Without the accompanying fix in place, this test would have failed, the import filter aborted
+ // loading.
+ xComponent = loadFromDesktop(sDataDirectory + "empty.doc");
+ CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.text.TextDocument"));
+ {
+ uno::Reference<frame::XModel> xModel(xComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aArgs = xModel->getArgs();
+ comphelper::SequenceAsHashMap aMap(aArgs);
+ OUString aFilterName;
+ aMap["FilterName"] >>= aFilterName;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: MS Word 97
+ // - Actual : MS WinWord 6.0
+ // i.e. opening worked, but saving back failed instead of producing a WW8 binary file.
+ CPPUNIT_ASSERT_EQUAL(OUString("MS Word 97"), aFilterName);
+ }
+ xComponent->dispose();
+
+ // Now test with default templates set
+
+ SfxObjectFactory::SetStandardTemplate("com.sun.star.presentation.PresentationDocument",
+ sDataDirectory + "impress.otp");
+ SfxObjectFactory::SetStandardTemplate("com.sun.star.text.TextDocument",
+ sDataDirectory + "writer.ott");
+ SfxObjectFactory::SetStandardTemplate("com.sun.star.sheet.SpreadsheetDocument",
+ sDataDirectory + "calc.ots");
+
+ xComponent = loadFromDesktop(sDataDirectory + "empty.pptx");
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xDoc(xComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::XDrawPages> xPages(xDoc->getDrawPages(), uno::UNO_SET_THROW);
+ uno::Reference<drawing::XDrawPage> xPage(xPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextRange> xBox(xPage->getByIndex(0), uno::UNO_QUERY_THROW);
+
+ // Make sure the template's text was loaded
+ CPPUNIT_ASSERT_EQUAL(OUString("Title of Impress template"), xBox->getString());
+ }
+ xComponent->dispose();
+
+ xComponent = loadFromDesktop(sDataDirectory + "empty.odt");
+ {
+ uno::Reference<text::XTextDocument> xDoc(xComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumerationAccess> xEA(xDoc->getText(), uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumeration> xEnum(xEA->createEnumeration(), uno::UNO_SET_THROW);
+ uno::Reference<text::XTextRange> xParagraph(xEnum->nextElement(), uno::UNO_QUERY_THROW);
+
+ // Make sure the template's text was loaded
+ CPPUNIT_ASSERT_EQUAL(OUString(u"Writer template’s first line"), xParagraph->getString());
+ }
+ xComponent->dispose();
+
+ xComponent = loadFromDesktop(sDataDirectory + "empty.ods");
+ {
+ uno::Reference<sheet::XSpreadsheetDocument> xDoc(xComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<sheet::XCellRangesAccess> xRA(xDoc->getSheets(), uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextRange> xC(xRA->getCellByPosition(0, 0, 0), uno::UNO_QUERY_THROW);
+
+ // Make sure the template's text was loaded
+ CPPUNIT_ASSERT_EQUAL(OUString(u"Calc template’s first cell"), xC->getString());
+ }
+ xComponent->dispose();
+
+ xComponent = loadFromDesktop(sDataDirectory + "empty.odp");
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xDoc(xComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::XDrawPages> xPages(xDoc->getDrawPages(), uno::UNO_SET_THROW);
+ uno::Reference<drawing::XDrawPage> xPage(xPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextRange> xBox(xPage->getByIndex(0), uno::UNO_QUERY_THROW);
+
+ // Make sure the template's text was loaded
+ CPPUNIT_ASSERT_EQUAL(OUString("Title of Impress template"), xBox->getString());
+ }
+ xComponent->dispose();
+
+ xComponent = loadFromDesktop(sDataDirectory + "empty.doc");
+ {
+ uno::Reference<text::XTextDocument> xDoc(xComponent, uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumerationAccess> xEA(xDoc->getText(), uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumeration> xEnum(xEA->createEnumeration(), uno::UNO_SET_THROW);
+ uno::Reference<text::XTextRange> xParagraph(xEnum->nextElement(), uno::UNO_QUERY_THROW);
+
+ // Make sure the template's text was loaded
+ CPPUNIT_ASSERT_EQUAL(OUString(u"Writer template’s first line"), xParagraph->getString());
+ }
+ xComponent->dispose();
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */