diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /filter/qa | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'filter/qa')
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 Binary files differnew file mode 100644 index 000000000..79af2b349 --- /dev/null +++ b/filter/qa/data/picture.pdf 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 Binary files differnew file mode 100644 index 000000000..d3027d17d --- /dev/null +++ b/filter/qa/unit/data/TransparentText.odg diff --git a/filter/qa/unit/data/attributeRedefinedTest.odp b/filter/qa/unit/data/attributeRedefinedTest.odp Binary files differnew file mode 100644 index 000000000..dfb814bfb --- /dev/null +++ b/filter/qa/unit/data/attributeRedefinedTest.odp diff --git a/filter/qa/unit/data/calc.ots b/filter/qa/unit/data/calc.ots Binary files differnew file mode 100644 index 000000000..d16d2307f --- /dev/null +++ b/filter/qa/unit/data/calc.ots 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 Binary files differnew file mode 100644 index 000000000..199a5f9d4 --- /dev/null +++ b/filter/qa/unit/data/impress.otp diff --git a/filter/qa/unit/data/preserve-jpg.odt b/filter/qa/unit/data/preserve-jpg.odt Binary files differnew file mode 100644 index 000000000..83768bd47 --- /dev/null +++ b/filter/qa/unit/data/preserve-jpg.odt diff --git a/filter/qa/unit/data/semi-transparent-fill.odg b/filter/qa/unit/data/semi-transparent-fill.odg Binary files differnew file mode 100644 index 000000000..713f48991 --- /dev/null +++ b/filter/qa/unit/data/semi-transparent-fill.odg diff --git a/filter/qa/unit/data/semi-transparent-line.odg b/filter/qa/unit/data/semi-transparent-line.odg Binary files differnew file mode 100644 index 000000000..2d28a694c --- /dev/null +++ b/filter/qa/unit/data/semi-transparent-line.odg diff --git a/filter/qa/unit/data/shape-nographic.odp b/filter/qa/unit/data/shape-nographic.odp Binary files differnew file mode 100644 index 000000000..43186d614 --- /dev/null +++ b/filter/qa/unit/data/shape-nographic.odp 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 Binary files differnew file mode 100644 index 000000000..1ded03150 --- /dev/null +++ b/filter/qa/unit/data/writer.ott 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: */ |