Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ once the version tag exists.
- The search button leaves the tool bar when the page cannot be searched,
rather than greying out - the same as the edit button.

### Fixed

- Flat XML documents (`.fodt`, `.fodp`, `.fods`, `.fodg`), `.otm`, `.xlt` and
`.xlm` can be picked in the document browser instead of being greyed out.
odrcore rendered them already; the app claimed no type that reached them.

## [1.40]

### Changed
Expand Down
4 changes: 4 additions & 0 deletions OpenDocumentReader.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
E24110312586349500800247 /* test.odt in Resources */ = {isa = PBXBuildFile; fileRef = E24110232586349500800247 /* test.odt */; };
E26C39392250DC6E009C484A /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E26C39382250DC6E009C484A /* WebKit.framework */; };
E2A17B0400000000000000A4 /* PageTabBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A17B0300000000000000A3 /* PageTabBarTests.swift */; };
E2A17B0500000000000000A5 /* DeclaredDocumentTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A17B0600000000000000A6 /* DeclaredDocumentTypesTests.swift */; };
E2A17B1000000000000000B0 /* test.ods in Resources */ = {isa = PBXBuildFile; fileRef = E2A17B1200000000000000B2 /* test.ods */; };
E2A17B1100000000000000B1 /* test.odp in Resources */ = {isa = PBXBuildFile; fileRef = E2A17B1300000000000000B3 /* test.odp */; };
E2A17B1400000000000000B4 /* test.csv in Resources */ = {isa = PBXBuildFile; fileRef = E2A17B1500000000000000B5 /* test.csv */; };
Expand Down Expand Up @@ -64,6 +65,7 @@
E24110232586349500800247 /* test.odt */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.odt; sourceTree = "<group>"; };
E26C39382250DC6E009C484A /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
E2A17B0300000000000000A3 /* PageTabBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageTabBarTests.swift; sourceTree = "<group>"; };
E2A17B0600000000000000A6 /* DeclaredDocumentTypesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeclaredDocumentTypesTests.swift; sourceTree = "<group>"; };
E2A17B1200000000000000B2 /* test.ods */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.ods; sourceTree = "<group>"; };
E2A17B1300000000000000B3 /* test.odp */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.odp; sourceTree = "<group>"; };
E2A17B1500000000000000B5 /* test.csv */ = {isa = PBXFileReference; lastKnownFileType = text; path = test.csv; sourceTree = "<group>"; };
Expand Down Expand Up @@ -171,6 +173,7 @@
E2A17B1900000000000000B9 /* test-encrypted.pdf */,
E22B252E2557F0E2001D0C52 /* OpenDocumentReaderTests.swift */,
E2A17B0300000000000000A3 /* PageTabBarTests.swift */,
E2A17B0600000000000000A6 /* DeclaredDocumentTypesTests.swift */,
E22B25302557F0E2001D0C52 /* Info.plist */,
);
path = OpenDocumentReaderTests;
Expand Down Expand Up @@ -388,6 +391,7 @@
files = (
E22B252F2557F0E2001D0C52 /* OpenDocumentReaderTests.swift in Sources */,
E2A17B0400000000000000A4 /* PageTabBarTests.swift in Sources */,
E2A17B0500000000000000A5 /* DeclaredDocumentTypesTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
87 changes: 87 additions & 0 deletions OpenDocumentReaderTests/DeclaredDocumentTypesTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import OdrCoreObjC
import UniformTypeIdentifiers
import XCTest

@testable import OpenDocumentReader

/// What the app offers itself for, held against odrcore's format table the way
/// OpenDocument.droid holds its manifest against the same table. The plist
/// cannot read the table, so this is what keeps the two from drifting.
class DeclaredDocumentTypesTests: XCTestCase {

/// Every `LSItemContentTypes` entry of the built app, not of a copy kept
/// here: the point is to test the plist that ships.
private lazy var claimed: [UTType] = {
let bundle = Bundle(for: DocumentViewController.self)
let entries = bundle.object(forInfoDictionaryKey: "CFBundleDocumentTypes") as? [[String: Any]] ?? []

return entries.flatMap { $0["LSItemContentTypes"] as? [String] ?? [] }.compactMap { UTType($0) }
}()

/// What the app offers itself for: odrcore's document formats, plus the two
/// non-document ones worth opening a viewer for. Narrower than everything
/// odrcore translates - it does images, media, fonts and archives too, and
/// the app does not want to be the handler for an mp3.
private var offeredFileTypes: [FileType] {
Odr.allFileTypes.compactMap { FileType(rawValue: $0.intValue) }
.filter { type in
guard Odr.capabilities(fileType: type).translateHtml else { return false }

return Odr.fileCategory(fileType: type) == .document
|| [.textFile, .commaSeparatedValues].contains(type)
}
}

func testEveryOfferedFormatIsClaimed() {
for type in offeredFileTypes {
for fileExtension in Odr.extensions(fileType: type) {
guard let resolved = UTType(filenameExtension: fileExtension) else {
XCTFail("iOS has no type for .\(fileExtension) (\(Odr.string(fileType: type)))")

continue
}

XCTAssertTrue(
claimed.contains(where: resolved.conforms(to:)),
".\(fileExtension) resolves to \(resolved.identifier), which conforms to nothing the app claims")
}
}
}

/// A dynamic type is what iOS invents for an extension it does not know. It
/// conforms to `public.data` and nothing else, so a file carrying one is
/// greyed out in the browser however broad the claims are.
func testNoOfferedFormatResolvesToADynamicType() {
for type in offeredFileTypes {
for fileExtension in Odr.extensions(fileType: type) {
let resolved = UTType(filenameExtension: fileExtension)

XCTAssertEqual(
resolved?.isDynamic, false,
".\(fileExtension) (\(Odr.string(fileType: type))) needs a UTImportedTypeDeclaration")
}
}
}

/// The formats odrcore cannot render must not be claimed through one of the
/// declarations above, or the browser offers a file the app then refuses.
func testDeclarationsCoverOnlyWhatOdrcoreRenders() throws {
let declarations =
Bundle(for: DocumentViewController.self)
.object(forInfoDictionaryKey: "UTImportedTypeDeclarations") as? [[String: Any]] ?? []

XCTAssertFalse(declarations.isEmpty)

for declaration in declarations {
let tags = declaration["UTTypeTagSpecification"] as? [String: Any] ?? [:]

for fileExtension in tags["public.filename-extension"] as? [String] ?? [] {
let type = Odr.fileType(extension: fileExtension)

XCTAssertTrue(
Odr.capabilities(fileType: type).translateHtml,
"odrcore does not render .\(fileExtension), so the app must not declare it")
}
}
}
}
145 changes: 145 additions & 0 deletions configs/full/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@
<string>public.image</string>
<string>public.audiovisual-content</string>
<string>public.composite-content</string>
<!-- The broad types above miss these: a system type that conforms
to none of them, and the six below that iOS has no type for
until UTImportedTypeDeclarations gives it one. -->
<string>com.microsoft.excel.xlt</string>
<string>com.microsoft.excel.xlm</string>
<string>org.oasis-open.opendocument.text-flat-xml</string>
<string>org.oasis-open.opendocument.presentation-flat-xml</string>
<string>org.oasis-open.opendocument.spreadsheet-flat-xml</string>
<string>org.oasis-open.opendocument.graphics-flat-xml</string>
<string>org.oasis-open.opendocument.text-master-template</string>
</array>
</dict>
</array>
Expand Down Expand Up @@ -104,5 +114,140 @@
</array>
<key>UISupportsDocumentBrowser</key>
<true/>
<!-- Formats odrcore renders that iOS has no type for. Without these the
document browser resolves them to a dynamic type, which conforms to
nothing we claim, and greys the file out. Imported rather than exported:
none of them is ours. -->
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>org.oasis-open.opendocument.text-flat-xml</string>
<key>UTTypeDescription</key>
<string>OpenDocument Text (Flat XML)</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fodt</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/vnd.oasis.opendocument.text-flat-xml</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.oasis-open.opendocument.presentation-flat-xml</string>
<key>UTTypeDescription</key>
<string>OpenDocument Presentation (Flat XML)</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fodp</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/vnd.oasis.opendocument.presentation-flat-xml</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.oasis-open.opendocument.spreadsheet-flat-xml</string>
<key>UTTypeDescription</key>
<string>OpenDocument Spreadsheet (Flat XML)</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fods</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/vnd.oasis.opendocument.spreadsheet-flat-xml</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.oasis-open.opendocument.graphics-flat-xml</string>
<key>UTTypeDescription</key>
<string>OpenDocument Drawing (Flat XML)</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fodg</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/vnd.oasis.opendocument.graphics-flat-xml</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.oasis-open.opendocument.text-master-template</string>
<key>UTTypeDescription</key>
<string>OpenDocument Master Document Template</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.zip-archive</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>otm</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/vnd.oasis.opendocument.text-master-template</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.microsoft.excel.xlm</string>
<key>UTTypeDescription</key>
<string>Microsoft Excel Macro Sheet</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.composite-content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>xlm</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>
Loading