ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj          |   
58 
 ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift                  |   
26 
 ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift           |  
177 ++-
 ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift            |  
229 +++
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift              |   
92 +
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift               |  
589 ++++++++++
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift            |  
287 ++++
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift |  
102 +
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift  |  
227 +++
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift                   |   
43 
 ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard           |   
63 -
 ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h            |    
1 
 12 files changed, 1864 insertions(+), 30 deletions(-)

New commits:
commit dd51c589774a88010d78530fc6d152a9af98f095
Author: jan Iversen <j...@libreoffice.org>
Date:   Thu Jan 11 15:36:16 2018 +0100

    iOS, Rendering document.
    
    This patch is with thanks to Jon Nermut.
    
    With this patch, the iPad renders documents as it should be
    rendered
    
    Change-Id: I54903fde3204b949d8c608842c004cd49a211d9a

diff --git a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj 
b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
index 13b0a4675179..650895263ca6 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
+++ b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
@@ -33,6 +33,13 @@
                39B091CE1E5F0BB800682A59 /* unorc in Resources */ = {isa = 
PBXBuildFile; fileRef = 39B08B9C1E5F0BB600682A59 /* unorc */; };
                39E950531FC9842000D82C49 /* source in Resources */ = {isa = 
PBXBuildFile; fileRef = 39E950521FC9842000D82C49 /* source */; };
                39EF4E2F1FA500C9001914AC /* PropertiesController.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 39EF4E2E1FA500C9001914AC /* 
PropertiesController.swift */; };
+               FCC2E3FA2004A01500CEB504 /* Document.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = FCC2E3F62004A01400CEB504 /* Document.swift */; };
+               FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F82004A01400CEB504 /* 
LibreOfficeKitWrapper.swift */; };
+               FCC2E3FD2004A01500CEB504 /* LOKitThread.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = FCC2E3F92004A01400CEB504 /* LOKitThread.swift 
*/; };
+               FCC2E3FF2004B59B00CEB504 /* DocumentTiledView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = FCC2E3FE2004B59B00CEB504 /* 
DocumentTiledView.swift */; };
+               FCC2E4012004B65E00CEB504 /* example.odt in Resources */ = {isa 
= PBXBuildFile; fileRef = FCC2E4002004B65E00CEB504 /* example.odt */; };
+               FCC2E4032004B72700CEB504 /* Util.swift in Sources */ = {isa = 
PBXBuildFile; fileRef = FCC2E4022004B72700CEB504 /* Util.swift */; };
+               FCC2E4052004B74000CEB504 /* AsyncUtil.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = FCC2E4042004B74000CEB504 /* AsyncUtil.swift */; 
};
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -68,7 +75,13 @@
                39E950521FC9842000D82C49 /* source */ = {isa = 
PBXFileReference; lastKnownFileType = folder; name = source; path = ../source; 
sourceTree = "<group>"; };
                39EE81531FA644E800B73AB8 /* Info.plist */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = 
Info.plist; sourceTree = "<group>"; };
                39EF4E2E1FA500C9001914AC /* PropertiesController.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = PropertiesController.swift; sourceTree = "<group>"; };
-               39FF0D4C200681F300A3657D /* LibreOfficeKitInit.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 
LibreOfficeKitInit.h; path = ../../include/LibreOfficeKit/LibreOfficeKitInit.h; 
sourceTree = "<group>"; };
+               FCC2E3F62004A01400CEB504 /* Document.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= Document.swift; sourceTree = "<group>"; };
+               FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = LibreOfficeKitWrapper.swift; sourceTree = "<group>"; };
+               FCC2E3F92004A01400CEB504 /* LOKitThread.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= LOKitThread.swift; sourceTree = "<group>"; };
+               FCC2E3FE2004B59B00CEB504 /* DocumentTiledView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= DocumentTiledView.swift; sourceTree = "<group>"; };
+               FCC2E4002004B65E00CEB504 /* example.odt */ = {isa = 
PBXFileReference; lastKnownFileType = file; name = example.odt; path = 
"../../android/default-document/example.odt"; sourceTree = "<group>"; };
+               FCC2E4022004B72700CEB504 /* Util.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; 
sourceTree = "<group>"; };
+               FCC2E4042004B74000CEB504 /* AsyncUtil.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncUtil.swift; 
sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -101,7 +114,6 @@
                3956B72D1FAB3DBF00BF5DE4 /* extra */ = {
                        isa = PBXGroup;
                        children = (
-                               39FF0D4C200681F300A3657D /* 
LibreOfficeKitInit.h */,
                                39E950521FC9842000D82C49 /* source */,
                                3975A8C91FBD70EE00A87B3A /* LibreOfficeKit.h */,
                        );
@@ -142,10 +154,12 @@
                397E08FC1E597BD8001374E0 /* LibreOfficeLight */ = {
                        isa = PBXGroup;
                        children = (
+                               FCC2E3F52004A01400CEB504 /* LOKit */,
                                39EE81531FA644E800B73AB8 /* Info.plist */,
                                39503A6F1F94C4AC00F19C78 /* 
lokit-Bridging-Header.h */,
                                397E08FD1E597BD8001374E0 /* AppDelegate.swift 
*/,
                                3992D8591E5B762A00BEA987 /* 
DocumentController.swift */,
+                               FCC2E3FE2004B59B00CEB504 /* 
DocumentTiledView.swift */,
                                39284DB21FA5F207006F43E4 /* 
DocumentActions.swift */,
                                39EF4E2E1FA500C9001914AC /* 
PropertiesController.swift */,
                                392ED9B21E5E4B03005C8435 /* 
ViewPrintManager.swift */,
@@ -159,6 +173,7 @@
                39B084E41E5F0B5200682A59 /* Resources */ = {
                        isa = PBXGroup;
                        children = (
+                               FCC2E4002004B65E00CEB504 /* example.odt */,
                                39022C201EDC2D0800100066 /* icudt60l.dat */,
                                39022C1E1EDC2AB000100066 /* share */,
                                39022C1C1EDC2A2C00100066 /* services */,
@@ -174,6 +189,18 @@
                        name = Resources;
                        sourceTree = SOURCE_ROOT;
                };
+               FCC2E3F52004A01400CEB504 /* LOKit */ = {
+                       isa = PBXGroup;
+                       children = (
+                               FCC2E4042004B74000CEB504 /* AsyncUtil.swift */,
+                               FCC2E3F62004A01400CEB504 /* Document.swift */,
+                               FCC2E3F82004A01400CEB504 /* 
LibreOfficeKitWrapper.swift */,
+                               FCC2E3F92004A01400CEB504 /* LOKitThread.swift 
*/,
+                               FCC2E4022004B72700CEB504 /* Util.swift */,
+                       );
+                       path = LOKit;
+                       sourceTree = "<group>";
+               };
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -257,6 +284,7 @@
                                39B08B9F1E5F0BB600682A59 /* oovbaapi.rdb in 
Resources */,
                                39B08B9D1E5F0BB600682A59 /* fundamentalrc in 
Resources */,
                                39B091CD1E5F0BB800682A59 /* udkapi.rdb in 
Resources */,
+                               FCC2E4012004B65E00CEB504 /* example.odt in 
Resources */,
                                39B08BD91E5F0BB600682A59 /* services.rdb in 
Resources */,
                                39B091CE1E5F0BB800682A59 /* unorc in Resources 
*/,
                                39022C1F1EDC2AB000100066 /* share in Resources 
*/,
@@ -271,11 +299,17 @@
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               FCC2E4032004B72700CEB504 /* Util.swift in 
Sources */,
                                392ED9B31E5E4B03005C8435 /* 
ViewPrintManager.swift in Sources */,
                                399648471E5B87DC00E73E83 /* 
ViewProperties.swift in Sources */,
+                               FCC2E3FC2004A01500CEB504 /* 
LibreOfficeKitWrapper.swift in Sources */,
                                39284DB31FA5F207006F43E4 /* 
DocumentActions.swift in Sources */,
                                3992D85A1E5B762A00BEA987 /* 
DocumentController.swift in Sources */,
+                               FCC2E3FD2004A01500CEB504 /* LOKitThread.swift 
in Sources */,
                                397E08FE1E597BD8001374E0 /* AppDelegate.swift 
in Sources */,
+                               FCC2E3FA2004A01500CEB504 /* Document.swift in 
Sources */,
+                               FCC2E3FF2004B59B00CEB504 /* 
DocumentTiledView.swift in Sources */,
+                               FCC2E4052004B74000CEB504 /* AsyncUtil.swift in 
Sources */,
                                39EF4E2F1FA500C9001914AC /* 
PropertiesController.swift in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
@@ -447,7 +481,10 @@
                                GCC_PRECOMPILE_PREFIX_HEADER = YES;
                                GCC_PREFIX_HEADER = 
"LibreOfficeLight/LibreOfficeLight-Prefix.pch";
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-                               HEADER_SEARCH_PATHS = "$(inherited)";
+                               HEADER_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "$(PROJECT_DIR)/../../include/**",
+                               );
                                INFOPLIST_FILE = LibreOfficeLight/Info.plist;
                                IPHONEOS_DEPLOYMENT_TARGET = 11.2;
                                LD_RUNPATH_SEARCH_PATHS = "$(inherited) 
@executable_path/Frameworks";
@@ -459,7 +496,7 @@
                                SWIFT_OPTIMIZATION_LEVEL = "-Onone";
                                SWIFT_SWIFT3_OBJC_INFERENCE = Off;
                                SWIFT_VERSION = 4.0;
-                               TARGETED_DEVICE_FAMILY = 2;
+                               TARGETED_DEVICE_FAMILY = "1,2";
                                VALID_ARCHS = "arm64 x86_64";
                        };
                        name = Debug;
@@ -477,6 +514,10 @@
                                ENABLE_TESTABILITY = NO;
                                GCC_PRECOMPILE_PREFIX_HEADER = YES;
                                GCC_PREFIX_HEADER = 
"LibreOfficeLight/LibreOfficeLight-Prefix.pch";
+                               HEADER_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "$(PROJECT_DIR)/../../include/**",
+                               );
                                INFOPLIST_FILE = LibreOfficeLight/Info.plist;
                                IPHONEOS_DEPLOYMENT_TARGET = 11.2;
                                LD_RUNPATH_SEARCH_PATHS = "$(inherited) 
@executable_path/Frameworks";
@@ -488,7 +529,7 @@
                                SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
                                SWIFT_SWIFT3_OBJC_INFERENCE = Off;
                                SWIFT_VERSION = 4.0;
-                               TARGETED_DEVICE_FAMILY = 2;
+                               TARGETED_DEVICE_FAMILY = "1,2";
                                VALID_ARCHS = "arm64 x86_64";
                        };
                        name = Release;
@@ -575,7 +616,10 @@
                                GCC_PRECOMPILE_PREFIX_HEADER = YES;
                                GCC_PREFIX_HEADER = 
"LibreOfficeLight/LibreOfficeLight-Prefix.pch";
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-                               HEADER_SEARCH_PATHS = "$(inherited)";
+                               HEADER_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "$(PROJECT_DIR)/../../include/**",
+                               );
                                INFOPLIST_FILE = LibreOfficeLight/Info.plist;
                                IPHONEOS_DEPLOYMENT_TARGET = 11.2;
                                LD_RUNPATH_SEARCH_PATHS = "$(inherited) 
@executable_path/Frameworks";
@@ -587,7 +631,7 @@
                                SWIFT_OPTIMIZATION_LEVEL = "-Onone";
                                SWIFT_SWIFT3_OBJC_INFERENCE = Off;
                                SWIFT_VERSION = 4.0;
-                               TARGETED_DEVICE_FAMILY = 2;
+                               TARGETED_DEVICE_FAMILY = "1,2";
                                VALID_ARCHS = "arm64 x86_64";
                        };
                        name = Simulator;
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
index 766aa4976a29..1804cd1e3ae3 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
@@ -9,7 +9,6 @@ import UIKit
 import Foundation
 
 
-
 // AppDelegate is a Delegate class that receives calls from the iOS
 // kernel, and theirby allows stop/start/sleep of the application
 @UIApplicationMain
@@ -22,7 +21,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate
     // sent when clicking on a OO document in another app
     // allowing this app to handle the document.
     // remark if the app is not started it will be started first
-    func application(_ app: UIApplication, open url: URL, options: 
[UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
+    func application(_ app: UIApplication,
+                     open url: URL, options: [UIApplicationOpenURLOptionsKey : 
Any] = [:])
+        -> Bool
     {
         let document = window?.rootViewController?.childViewControllers[0] as! 
DocumentController
         document.doOpen(url)
@@ -33,7 +34,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate
 
     // this function is called when the app is first started (loaded from 
EEProm)
     // it initializes the LO system and prepares for a normal run
-    func application(_ application: UIApplication, 
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: 
Any]?) -> Bool
+    func application(_ application: UIApplication,
+                     didFinishLaunchingWithOptions launchOptions:
+                         [UIApplicationLaunchOptionsKey: Any]?)
+        -> Bool
     {
         // Get version info
         let appInfo = Bundle.main.infoDictionary! as 
Dictionary<String,AnyObject>
@@ -46,7 +50,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate
         defaults.synchronize()
 
         // start LibreOfficeKit
-        BridgeLOkit_Init(Bundle.main.bundlePath)
+        let _ = LOKitThread.instance
+
         return true
     }
 
@@ -55,8 +60,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate
     // Sent when the application is about to move from active to inactive 
state.
     // This can occur for certain types of temporary interruptions
     // (such as an incoming phone call or SMS message)
-    // or when the user quits the application and it begins the transition to 
the background state.
-    // Use this method to pause ongoing tasks, disable timers, and invalidate 
graphics rendering callbacks.
+    // or when the user quits the application and it begins the transition
+    // jto the background state.
+    // Use this method to pause ongoing tasks, disable timers,
+    // and invalidate graphics rendering callbacks.
     func applicationWillResignActive(_ application: UIApplication)
     {
         // NOT used in this App
@@ -66,13 +73,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate
 
     // Sent when the application enters background (hipernating)
     // Use this method to release shared resources, save user data, invalidate 
timers,
-    // and store enough application state information to restore your 
application to its current state
-    // in case it is terminated later.
+    // and store enough application state information to restore your 
application
+    // to its current state jin case it is terminated later.
     // If your application supports background execution,
     // this method is called instead of applicationWillTerminate: when the 
user quits.
     func applicationDidEnterBackground(_ application: UIApplication)
     {
-        let document = window?.rootViewController?.childViewControllers[0] as! 
DocumentController
+        let document = window?.rootViewController?.childViewControllers[0]
+            as! DocumentController
         document.Hipernate()
     }
 
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
index 273b0e04ea97..181e707a3da5 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
@@ -8,13 +8,16 @@
 import UIKit
 
 
-
 // DocumentController is the main viewer in the app, it displays the selected
 // documents and holds a top entry to view the properties as well as a normal
 // menu to handle global actions
 // It is a delegate class to receive Menu events as well as file handling 
events
 class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewControllerDelegate
 {
+    var document: DocumentHolder? = nil
+
+    var documentView: DocumentTiledView? = nil
+
     // *** Handling of DocumentController
     // this is normal functions every controller must implement
 
@@ -22,6 +25,10 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
     // holds known document types
     var KnownDocumentTypes : [String] = []
 
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var mask: UIView!
+    @IBOutlet weak var progressBar: UIProgressView!
+    @IBOutlet weak var searchBar: UISearchBar!
 
     // called once controller is loaded
     override func viewDidLoad()
@@ -36,9 +43,19 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
             let x = ((dict["UTTypeTagSpecification"]  as! 
NSDictionary)["public.filename-extension"] as! NSArray)
             KnownDocumentTypes.append( x[0] as! String )
         }
+        LOKitThread.instance.progressDelegate = self
     }
 
+    override func viewDidAppear(_ animated: Bool)
+    {
+        let res = Bundle.main.url(forResource: "example", withExtension: "odt")
+        //let res = Bundle.main.url(forResource: "example2", withExtension: 
"docx")
 
+        if let exampleDoc = res
+        {
+            self.doOpen(exampleDoc)
+        }
+    }
 
     // called when there is a memory constraint
     override func didReceiveMemoryWarning()
@@ -47,6 +64,14 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
         // not used in this App
     }
 
+    @IBAction func searchIconTapped(_ sender: Any)
+    {
+        searchBar.isHidden = !searchBar.isHidden
+        if (!searchBar.isHidden)
+        {
+            searchBar.becomeFirstResponder()
+        }
+    }
 
 
     // *** Handling of Background (hipernate)
@@ -60,7 +85,7 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
     // Moving to hipernate
     public func Hipernate() -> Void
     {
-        BridgeLOkit_Hipernate()
+        //BridgeLOkit_Hipernate() // FIXME
     }
 
 
@@ -68,7 +93,7 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
     // Moving back to foreground
     public func LeaveHipernate() -> Void
     {
-        BridgeLOkit_LeaveHipernate()
+        //BridgeLOkit_LeaveHipernate() // FIXME
     }
 
 
@@ -318,9 +343,153 @@ class DocumentController: UIViewController, MenuDelegate, 
UIDocumentBrowserViewC
     // Real open and presentation of document
     public func doOpen(_ docURL : URL)
     {
-        BridgeLOkit_open(docURL.absoluteString);
+        LOKitThread.instance.documentLoad(url: docURL.absoluteString)
+        {
+            doc, error in
+
+            if let document = doc
+            {
+
+                runOnMain
+                {
+                    self.setDocument(doc: document)
+                }
+            }
+            else
+            {
+                // TODO - alert user of failure
+
+            }
+        }
+
+        /* FIXME
         BridgeLOkit_Sizing(4, 4, 256, 256);
+ */
+    }
+
+    /// Sets the document to use and set's up it's view. Should be called on 
the main thread
+    public func setDocument(doc: DocumentHolder)
+    {
+        if let existingDoc = self.document
+        {
+            // TODO - cleanup
+            self.document = nil
+        }
+        if let exisitingView = self.documentView
+        {
+            exisitingView.removeFromSuperview()
+            self.documentView = nil // forces the close of the view and it's 
held documents before we setup the new one
+        }
+
+        // setup the new doc view
+        self.document = doc
+
+        let frameToUse = self.scrollView.frame
+
+        let docView = DocumentTiledView(frame: frameToUse, document: doc, 
scale: 1.0)
+
+        self.scrollView.addSubview(docView)
+        self.scrollView.contentSize = docView.frame.size
+        self.documentView = docView
+
+        // debugging view borders
+        /*
+        self.scrollView.layer.borderColor = UIColor.red.cgColor
+        self.scrollView.layer.borderWidth = 1.0
+        docView.layer.borderColor = UIColor.green.cgColor
+        docView.layer.borderWidth = 1.0
+        */
+    }
+
+    // MARK: - UIScrollViewDelegate
+}
+
+extension DocumentController: UIScrollViewDelegate
+{
+    // return a view that will be scaled. if delegate returns nil, nothing 
happens
+    func viewForZooming(in scrollView: UIScrollView) -> UIView?
+    {
+        return self.documentView
+    }
+
+    // called before the scroll view begins zooming its content
+    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: 
UIView?)
+    {
+        print("scrollViewWillBeginZooming 
currentScale=\(scrollView.zoomScale)")
+    }
+
+    // scale between minimum and maximum. called after any 'bounce' animations
+    func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: 
UIView?, atScale scale: CGFloat)
+    {
+        print("scrollViewDidEndZooming scale=\(scale)")
+        self.documentView?.scrollViewDidEndZooming(scrollView, with: view, 
atScale: scale)
+    }
+}
+
+    // MARK: -  UIKeyInput
+//    public var hasText: Bool
+//    {
+//        true
+//    }
+//
+//
+//    public func insertText(_ text: String)
+//    {
+//
+//    }
+//
+//    public func deleteBackward()
+//    {
+//
+//    }
+
+extension DocumentController: ProgressDelegate
+{
+    // MARK: - ProgressDelegate
+    func statusIndicatorStart()
+    {
+        self.mask?.isHidden = false
+        self.progressBar?.isHidden = false
+        self.progressBar?.progress = 0.0
+    }
+
+    func statusIndicatorFinish()
+    {
+        // what would be nice would be to be able to wait until the initial 
tiles have rendered...
+        self.mask?.isHidden = true
+        self.progressBar?.isHidden = true
+    }
+
+    func statusIndicatorSetValue(value: Double)
+    {
+        self.progressBar?.progress = Float(value) / 100.0
     }
 }
 
+extension DocumentController: UISearchBarDelegate
+{
+    // called when text changes (including clear)
+    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
+    {
+
+    }
+
+
+    // called when keyboard search button pressed
+    func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
+    {
+        if let text = searchBar.text
+        {
+            if text.count > 0
+            {
+                document?.search(searchString: text, forwardDirection: true, 
from: CGPoint(x:0, y:0) )
+            }
+        }
+    }
+
+    func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
+    {
+        searchBar.isHidden = true
+    }
+}
 
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
new file mode 100644
index 000000000000..b49a8b0eb71f
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
@@ -0,0 +1,229 @@
+//
+// 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/.
+//
+
+import UIKit
+import QuartzCore
+
+
+class DocumentTiledLayer : CATiledLayer
+{
+    override class func fadeDuration() -> CFTimeInterval
+    {
+        return 0
+    }
+}
+
+open class CachedRender
+{
+    open let x: CGFloat
+    open let y: CGFloat
+    open let scale: CGFloat
+    open let image: CGImage
+
+    public init(x: CGFloat, y: CGFloat, scale: CGFloat, image: CGImage)
+    {
+        self.x = x
+        self.y = y
+        self.scale = scale
+        self.image = image
+    }
+}
+
+
+class DocumentTiledView: UIView
+{
+    var myScale: CGFloat
+
+    weak var document: DocumentHolder? = nil
+
+    let initialSize: CGSize
+    let docSize: CGSize
+    let initialScaleFactor: CGFloat
+
+    var drawCount = 0
+
+    let drawLock = NSLock()
+
+    // Create a new view with the desired frame and scale.
+    public init(frame: CGRect, document: DocumentHolder, scale: CGFloat)
+    {
+
+
+        self.document = document
+
+
+        myScale = scale
+        initialSize = frame.size
+        var size = document.sync { $0.getDocumentSizeAsCGSize() }
+
+        // avoid divide by zero crashes
+        if (size.width == 0)
+        {
+            size.width = 1
+        }
+        if (size.height == 0)
+        {
+            size.height = 1
+        }
+        self.docSize = size
+        initialScaleFactor = (docSize.width / initialSize.width)
+        let scaledFrame = CGRect(x: 0, y: 0, width: frame.width, height: 
frame.width * (docSize.height / docSize.width))
+
+        print("DocumentTiledView.init frame=\(frame.desc) \n  
scaledFrame=\(scaledFrame.desc)\n  docSize=\(docSize) \n  
initialScaleFactor=\(initialScaleFactor)")
+        super.init(frame: scaledFrame)
+
+        //self.contentScaleFactor = 1.0
+
+        if let tiledLayer = self.layer as? CATiledLayer
+        {
+            tiledLayer.levelsOfDetail = 4
+            tiledLayer.levelsOfDetailBias = 7
+            tiledLayer.tileSize = CGSize(width: 1024.0, height: 1024.0)
+            //tiledLayer.tileSize = CGSize(width: 512.0, height: 512.0)
+        }
+
+    }
+
+    required init?(coder aDecoder: NSCoder)
+    {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+
+
+    override class var layerClass : AnyClass
+    {
+        return DocumentTiledLayer.self
+    }
+
+
+    override func draw(_ r: CGRect)
+    {
+        // UIView uses the existence of -drawRect: to determine if it should 
allow its CALayer
+        // to be invalidated, which would then lead to the layer creating a 
backing store and
+        // -drawLayer:inContext: being called.
+        // By implementing an empty -drawRect: method, we allow UIKit to 
continue to implement
+        // this logic, while doing our real drawing work inside of 
-drawLayer:inContext:
+    }
+
+    // Draw the CGPDFPageRef into the layer at the correct scale.
+    override func draw(_ layer: CALayer, in context: CGContext)
+    {
+//        if self.superview == nil
+//        {
+//            // check that we are still active - ios is doing some really 
funny things where this method gets called after dealloc which causes bad bad 
karma
+//            return
+//        }
+        guard let document = self.document else
+        {
+            return
+        }
+
+        guard let tiledLayer = layer as? CATiledLayer else { return }
+
+
+
+        let tileSize: CGSize = tiledLayer.tileSize
+        let box: CGRect = context.boundingBoxOfClipPath
+        let ctm: CGAffineTransform = context.ctm
+
+        drawLock.lock()
+        defer { drawLock.unlock() }
+
+        drawCount += 1
+        let filename = "tile\(drawCount).png"
+
+        print("drawLayer \(filename)\n  bounds=\(layer.bounds.desc)\n  
ctm.a=\(ctm.a)\n  tileSize=\(tileSize)\n   box=\(box.desc)")
+
+        //context.setFillColor(UIColor.white.cgColor)
+        context.setFillColor(UIColor.blue.cgColor)
+        context.fill(box)
+        context.saveGState()
+
+        context.interpolationQuality = CGInterpolationQuality.high
+        context.setRenderingIntent(CGColorRenderingIntent.defaultIntent)
+
+        // This is where the magic happens
+
+        let pageRect = box.applying(CGAffineTransform(scaleX: 
initialScaleFactor, y: initialScaleFactor ))
+        print("  pageRect: \(pageRect.desc)")
+
+        // Figure out how many pixels we need for the dimensions of our tile
+        // tileSize represents a "full size" one in pixels
+
+        //let fullSizeTileInPoints = CGSize(width: CGFloat(tileSize.width) / 
ctm.a, height: CGFloat(tileSize.height) / ctm.a)
+        //let cropRectTileFraction = CGSize(width: box.size.width / 
fullSizeTileInPoints.width, height: box.size.height / 
fullSizeTileInPoints.height)
+        //let bitmapSize = CGSize(width: tileSize.width * 
cropRectTileFraction.width, height: tileSize.height * 
cropRectTileFraction.height)
+
+        let canvasSize = tileSize; //CGSize(width:512, height:512) // FIXME - 
this needs to be calculated
+
+        // we have to do the call synchronously, as the tile has to be painted 
now, on the current thread
+        // TODO - cache the image, and check the cache before we do the sync 
call
+        let image = document.sync {
+            $0.paintTileToImage(canvasSize: canvasSize, tileRect: pageRect)
+        }
+
+        if let img = image
+        {
+            // Debugging: write the file to disk
+            /*
+            if let data = UIImagePNGRepresentation(img)
+            {
+                let filename = 
getDocumentsDirectory().appendingPathComponent(filename)
+                try? data.write(to: filename)
+                print("Wrote tile to: \(filename)")
+            }
+            */
+
+            // We use the UIImage draw function as it automatically handles 
the flipping of the co-ordinate system for us.
+            UIGraphicsPushContext(context);
+            img.draw(in: box)
+            UIGraphicsPopContext()
+        }
+
+        context.restoreGState()
+
+
+    }
+
+
+
+    /*
+    fileprivate func emptyCache()
+    {
+        cachedRenders.removeAll()
+    }
+
+    fileprivate func pruneCache()
+    {
+        let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
+        while cachedRenders.count > max
+        {
+            cachedRenders.popFirst()
+        }
+    }
+ */
+
+    deinit
+    {
+        self.document = nil
+
+    }
+
+
+    func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: 
UIView?, atScale scale: CGFloat)
+    {
+        //self.setNeedsDisplay()
+    }
+
+
+//    override func pressesBegan
+//    {
+//
+//    }
+}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift
new file mode 100644
index 000000000000..52f8c1bddced
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift
@@ -0,0 +1,92 @@
+//
+// 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/.
+//
+
+import Foundation
+
+
+public typealias Runnable = () -> ()
+
+/// Runs the closure on a queued background thread
+public func runInBackground(_ runnable: @escaping Runnable)
+{
+    DispatchQueue.global(qos: .background).async(execute: runnable)
+}
+
+
+/// Runs the closure on the UI (main) thread. Exceptions are caught and logged
+public func runOnMain(_ runnable: @escaping () -> ())
+{
+    DispatchQueue.main.async(execute: runnable)
+}
+
+/// Returns true if we are on the Main / UI thread
+public func isMainThread() -> Bool
+{
+    return Thread.isMainThread
+}
+
+/// Runs tasks in a serial way on a single thread.
+/// Why wouldn't we just use DispatchQueue or NSOperationQueue to do this?
+/// Because neither guarantee running their tasks on the same thread all the 
time.
+/// And in fact DispatchQueue will try and run sync tasks on the current 
thread where it can.
+/// Both classes try and abstract the thread away, whereas we have to use the 
same thread, or we end up with deadlocks in LOKit
+public class SingleThreadedQueue: Thread
+{
+    public init(name: String)
+    {
+        super.init()
+        self.name = name
+        self.start()
+    }
+
+    override public func main()
+    {
+        // You need the NSPort here because a runloop with no sources or ports 
registered with it
+        // will simply exit immediately instead of running forever.
+        let keepAlive = Port()
+        let rl = RunLoop.current
+        keepAlive.schedule(in: rl, forMode: .commonModes)
+
+        rl.run()
+    }
+
+    /// Run the task on the serial queue, and return immediately
+    public func async( _ runnable: @escaping Runnable)
+    {
+        let operation = BlockOperation {
+            runnable()
+        }
+        async(operation: operation)
+    }
+
+    /// Run the task on the serial queue, and return immediately
+    public func async( operation: Operation)
+    {
+        if ( Thread.current == self)
+        {
+            operation.start();
+        }
+        else
+        {
+            operation.perform(#selector(Operation.start), on: self, with: nil, 
waitUntilDone: false)
+        }
+    }
+
+    public func sync<R>( _ closure: @escaping () -> R ) -> R
+    {
+        var ret: R! = nil
+        let op = BlockOperation {
+            ret = closure();
+        }
+        async(operation: op)
+        op.waitUntilFinished()
+        return ret
+    }
+
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift
new file mode 100644
index 000000000000..8f54704dc251
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift
@@ -0,0 +1,589 @@
+//
+// 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/.
+//
+
+import Foundation
+import UIKit
+import QuartzCore
+
+
+/// The Document class represents one loaded document instance
+/// Obtained through LibreOffice.documentLoad()
+open class Document
+{
+    private let pDoc: UnsafeMutablePointer<LibreOfficeKitDocument>
+    private let docClass: LibreOfficeKitDocumentClass
+
+    internal init(pDoc: UnsafeMutablePointer<LibreOfficeKitDocument>)
+    {
+        self.pDoc = pDoc
+        self.docClass = pDoc.pointee.pClass.pointee
+    }
+
+    /**
+     * Stores the document's persistent data to a URL and
+     * continues to be a representation of the old URL.
+     *
+     * @param pUrl the location where to store the document
+     * @param pFormat the format to use while exporting, when omitted, then 
deducted from pURL's extension
+     * @param pFilterOptions options for the export filter, e.g. SkipImages.
+     *        Another useful FilterOption is "TakeOwnership".  It is consumed
+     *        by the saveAs() itself, and when provided, the document identity
+     *        changes to the provided pUrl - meaning that '.uno:ModifiedStatus'
+     *        is triggered as with the "Save As..." in the UI.
+     *        "TakeOwnership" mode must not be used when saving to PNG or PDF.
+     */
+    public func saveAs(url: String, format: String? = nil, filterOptions: 
String? = nil) -> Bool
+    {
+        return docClass.saveAs(pDoc, url, format, filterOptions) != 0
+    }
+
+    /**
+     * Get document type.
+     *
+     * @since LibreOffice 6.0
+     * @return an element of the LibreOfficeKitDocumentType enum.
+     */
+    public func getDocumentType() -> LibreOfficeKitDocumentType
+    {
+        return LibreOfficeKitDocumentType(rawValue: 
LibreOfficeKitDocumentType.RawValue(docClass.getDocumentType(pDoc)))
+    }
+
+    /**
+     * Get number of part that the document contains.
+     *
+     * Part refers to either individual sheets in a Calc, or slides in Impress,
+     * and has no relevance for Writer.
+     */
+    public func getParts() -> Int32
+    {
+        return docClass.getParts(pDoc);
+    }
+
+    public func initializeForRendering()
+    {
+        docClass.initializeForRendering(pDoc, "") // TODO: arguments??
+    }
+
+    /**
+     * Get the logical rectangle of each part in the document.
+     *
+     * A part refers to an individual page in Writer and has no relevant for
+     * Calc or Impress.
+     *
+     * @return a rectangle list, using the same format as
+     * LOK_CALLBACK_TEXT_SELECTION.
+     */
+    public func getPartRectanges() -> String
+    {
+        return toString( docClass.getPartPageRectangles(pDoc) ) ?? ""
+
+        // TODO: convert to CGRects? Comes out like "284, 284, 11906, 16838; 
284, 17406, 11906, 16838; 284, 34528, 11906, 16838"
+
+    }
+
+    /// Get the current part of the document.
+    public func getPart() -> Int32
+    {
+        return docClass.getPart(pDoc);
+    }
+
+    /// Set the current part of the document.
+    public func setPart( nPart: Int32 )
+    {
+        docClass.setPart(pDoc, nPart);
+    }
+
+    /// Get the current part's name.
+    public func getPartName( nPart: Int32) -> String?
+    {
+        return toString( docClass.getPartName(pDoc, nPart) )
+
+    }
+
+    /// Get the current part's hash.
+    public func getPartHash( nPart: Int32 ) -> String?
+    {
+        return toString( docClass.getPartHash(pDoc, nPart) )
+
+    }
+
+    public func setPartMode( nMode: Int32 )
+    {
+        docClass.setPartMode( pDoc, nMode);
+    }
+
+    /**
+     * Renders a subset of the document to a pre-allocated buffer.
+     *
+     * Note that the buffer size and the tile size implicitly supports
+     * rendering at different zoom levels, as the number of rendered pixels and
+     * the rendered rectangle of the document are independent.
+     *
+     * @param pBuffer pointer to the buffer, its size is determined by 
nCanvasWidth and nCanvasHeight.
+     * @param nCanvasWidth number of pixels in a row of pBuffer.
+     * @param nCanvasHeight number of pixels in a column of pBuffer.
+     * @param nTilePosX logical X position of the top left corner of the 
rendered rectangle, in TWIPs.
+     * @param nTilePosY logical Y position of the top left corner of the 
rendered rectangle, in TWIPs.
+     * @param nTileWidth logical width of the rendered rectangle, in TWIPs.
+     * @param nTileHeight logical height of the rendered rectangle, in TWIPs.
+     */
+    public func paintTile( pBuffer: UnsafeMutablePointer<UInt8>,
+        canvasWidth: Int32,
+        canvasHeight: Int32,
+        tilePosX: Int32,
+        tilePosY: Int32,
+        tileWidth: Int32,
+        tileHeight: Int32)
+    {
+        print("paintTile canvasWidth=\(canvasWidth) 
canvasHeight=\(canvasHeight) tilePosX=\(tilePosX) tilePosY=\(tilePosY) 
tileWidth=\(tileWidth) tileHeight=\(tileHeight) ")
+        return docClass.paintTile(pDoc, pBuffer, canvasWidth, canvasHeight,
+                                tilePosX, tilePosY, tileWidth, tileHeight);
+    }
+
+    /**
+     * Renders a window (dialog, popup, etc.) with give id
+     *
+     * @param nWindowId
+     * @param pBuffer Buffer with enough memory allocated to render any dialog
+     * @param x x-coordinate from where the dialog should start painting
+     * @param y y-coordinate from where the dialog should start painting
+     * @param width The width of the dialog image to be painted
+     * @param height The height of the dialog image to be painted
+     */
+    public func paintWindow( nWindowId: UInt32,
+        pBuffer: UnsafeMutablePointer<UInt8>,
+        x: Int32,
+        y: Int32,
+        width: Int32,
+        height: Int32)
+    {
+        return docClass.paintWindow(pDoc, nWindowId, pBuffer, x, y, width, 
height);
+    }
+
+    /**
+     * Posts a command to the window (dialog, popup, etc.) with given id
+     *
+     * @param nWindowid
+     */
+    public func postWindow( nWindowId: UInt32, nAction: Int32)
+    {
+        return docClass.postWindow(pDoc, nWindowId, nAction);
+    }
+
+    /**
+     * Gets the tile mode: the pixel format used for the pBuffer of 
paintTile().
+     *
+     * @return an element of the LibreOfficeKitTileMode enum.
+     */
+    public func getTileMode() -> LibreOfficeKitTileMode
+    {
+        return LibreOfficeKitTileMode(rawValue: 
LibreOfficeKitTileMode.RawValue(docClass.getTileMode(pDoc)));
+    }
+
+    /// Get the document sizes in TWIPs.
+    public func getDocumentSize() -> (Int, Int)
+    {
+        print(Thread.isMainThread)
+        // long* pWidth, long* pHeight
+        var pWidth: Int = 0
+        var pHeight: Int = 0
+        docClass.getDocumentSize(pDoc, &pWidth, &pHeight);
+        return (pWidth, pHeight)
+    }
+
+    /**
+     * Initialize document for rendering.
+     *
+     * Sets the rendering and document parameters to default values that are
+     * needed to render the document correctly using tiled rendering. This
+     * method has to be called right after documentLoad() in case any of the
+     * tiled rendering methods are to be used later.
+     *
+     * Example argument string for text documents:
+     *
+     * {
+     *     ".uno:HideWhitespace":
+     *     {
+     *         "type": "boolean",
+     *         "value": "true"
+     *     }
+     * }
+     *
+     * @param pArguments arguments of the rendering
+     */
+    public func initializeForRendering(arguments: String? = nil)
+    {
+        docClass.initializeForRendering(pDoc, arguments);
+    }
+
+    /**
+     * Registers a callback. LOK will invoke this function when it wants to
+     * inform the client about events.
+     *
+     * @param pCallback the callback to invoke
+     * @param pData the user data, will be passed to the callback on invocation
+     */
+    public func registerCallback( callback: @escaping LibreOfficeCallback ) -> 
Int
+    {
+        let ret = Callbacks.register(callback: callback)
+        let pointer = UnsafeMutableRawPointer(bitPattern: ret)
+        docClass.registerCallback(pDoc, callbackFromLibreOffice, pointer)
+        return ret
+    }
+
+    /**
+     * Posts a keyboard event to the focused frame.
+     *
+     * @param nType Event type, like press or release.
+     * @param nCharCode contains the Unicode character generated by this event 
or 0
+     * @param nKeyCode contains the integer code representing the key of the 
event (non-zero for control keys)
+     */
+    public func postKeyEvent(nType: Int32, nCharCode: Int32, nKeyCode: Int32)
+    {
+        docClass.postKeyEvent(pDoc, nType, nCharCode, nKeyCode);
+    }
+
+    /**
+     * Posts a keyboard event to the dialog
+     *
+     * @param nWindowId
+     * @param nType Event type, like press or release.
+     * @param nCharCode contains the Unicode character generated by this event 
or 0
+     * @param nKeyCode contains the integer code representing the key of the 
event (non-zero for control keys)
+     */
+    public func postWindowKeyEvent( nWindowId: UInt32, nType: Int32, 
nCharCode: Int32, nKeyCode: Int32)
+    {
+        docClass.postWindowKeyEvent(pDoc, nWindowId, nType, nCharCode, 
nKeyCode);
+    }
+
+    /**
+     * Posts a mouse event to the document.
+     *
+     * @param nType Event type, like down, move or up.
+     * @param nX horizontal position in document coordinates
+     * @param nY vertical position in document coordinates
+     * @param nCount number of clicks: 1 for single click, 2 for double click
+     * @param nButtons: which mouse buttons: 1 for left, 2 for middle, 4 right
+     * @param nModifier: which keyboard modifier: (see include/vcl/vclenum.hxx 
for possible values)
+     */
+    public func postMouseEvent( nType: Int32, nX: Int32, nY: Int32, nCount: 
Int32, nButtons: Int32, nModifier: Int32)
+    {
+        docClass.postMouseEvent(pDoc, nType, nX, nY, nCount, nButtons, 
nModifier);
+    }
+
+    /**
+     * Posts a mouse event to the window with given id.
+     *
+     * @param nWindowId
+     * @param nType Event type, like down, move or up.
+     * @param nX horizontal position in document coordinates
+     * @param nY vertical position in document coordinates
+     * @param nCount number of clicks: 1 for single click, 2 for double click
+     * @param nButtons: which mouse buttons: 1 for left, 2 for middle, 4 right
+     * @param nModifier: which keyboard modifier: (see include/vcl/vclenum.hxx 
for possible values)
+     */
+    public func postWindowMouseEvent(nWindowId: UInt32, nType: Int32, nX: 
Int32, nY: Int32, nCount: Int32, nButtons: Int32, nModifier: Int32)
+    {
+        docClass.postWindowMouseEvent(pDoc, nWindowId, nType, nX, nY, nCount, 
nButtons, nModifier);
+    }
+
+    /**
+     * Posts an UNO command to the document.
+     *
+     * Example argument string:
+     *
+     * {
+     *     "SearchItem.SearchString":
+     *     {
+     *         "type": "string",
+     *         "value": "foobar"
+     *     },
+     *     "SearchItem.Backward":
+     *     {
+     *         "type": "boolean",
+     *         "value": "false"
+     *     }
+     * }
+     *
+     * @param pCommand uno command to be posted to the document, like 
".uno:Bold"
+     * @param pArguments arguments of the uno command.
+     */
+    public func postUnoCommand(command: String, arguments: String? = nil, 
notifyWhenFinished: Bool = false)
+    {
+        docClass.postUnoCommand(pDoc, command, arguments, notifyWhenFinished);
+    }
+
+    /**
+     * Sets the start or end of a text selection.
+     *
+     * @param nType @see LibreOfficeKitSetTextSelectionType
+     * @param nX horizontal position in document coordinates
+     * @param nY vertical position in document coordinates
+     */
+    public func setTextSelection( nType: Int32, nX: Int32, nY: Int32)
+    {
+        docClass.setTextSelection(pDoc, nType, nX, nY);
+    }
+
+    /**
+     * Gets the currently selected text.
+     *
+     * @param pMimeType suggests the return format, for example 
text/plain;charset=utf-8.
+     * @param pUsedMimeType output parameter to inform about the determined 
format (suggested one or plain text).
+     */
+    // FIXME - work out how to use an inout param for usedMimeType
+    public func getTextSelection(mimeType: String, usedMimeType: 
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? = nil) -> String?
+    {
+        return toString( docClass.getTextSelection(pDoc, mimeType, 
usedMimeType) );
+    }
+
+    /**
+     * Pastes content at the current cursor position.
+     *
+     * @param pMimeType format of pData, for example text/plain;charset=utf-8.
+     * @param pData the actual data to be pasted.
+     * @return if the supplied data was pasted successfully.
+     */
+    public func paste(mimeType: String, data: String, size: Int) -> Bool
+    {
+        return docClass.paste(pDoc, mimeType, data, size);
+    }
+
+    /**
+     * Adjusts the graphic selection.
+     *
+     * @param nType @see LibreOfficeKitSetGraphicSelectionType
+     * @param nX horizontal position in document coordinates
+     * @param nY vertical position in document coordinates
+     */
+    public func setGraphicSelection( nType: Int32, nX: Int32, nY: Int32)
+    {
+        docClass.setGraphicSelection(pDoc, nType, nX, nY);
+    }
+
+    /**
+     * Gets rid of any text or graphic selection.
+     */
+    public func resetSelection()
+    {
+        docClass.resetSelection(pDoc);
+    }
+
+    /**
+     * Returns a json mapping of the possible values for the given command
+     * e.g. {commandName: ".uno:StyleApply", commandValues: {"familyName1" : 
["list of style names in the family1"], etc.}}
+     * @param pCommand a uno command for which the possible values are 
requested
+     * @return {commandName: unoCmd, commandValues: {possible_values}}
+     */
+    public func getCommandValues(command: String) -> String?
+    {
+        return toString(docClass.getCommandValues(pDoc, command));
+    }
+
+    /**
+     * Save the client's view so that we can compute the right zoom level
+     * for the mouse events. This only affects CALC.
+     * @param nTilePixelWidth - tile width in pixels
+     * @param nTilePixelHeight - tile height in pixels
+     * @param nTileTwipWidth - tile width in twips
+     * @param nTileTwipHeight - tile height in twips
+     */
+    public func setClientZoom(
+        nTilePixelWidth: Int32,
+        nTilePixelHeight: Int32,
+        nTileTwipWidth: Int32,
+        nTileTwipHeight: Int32)
+    {
+        docClass.setClientZoom(pDoc, nTilePixelWidth, nTilePixelHeight, 
nTileTwipWidth, nTileTwipHeight);
+    }
+
+    /**
+     * Inform core about the currently visible area of the document on the
+     * client, so that it can perform e.g. page down (which depends on the
+     * visible height) in a sane way.
+     *
+     * @param nX - top left corner horizontal position
+     * @param nY - top left corner vertical position
+     * @param nWidth - area width
+     * @param nHeight - area height
+     */
+    public func setClientVisibleArea( nX: Int32, nY: Int32, nWidth: Int32, 
nHeight: Int32)
+    {
+        docClass.setClientVisibleArea(pDoc, nX, nY, nWidth, nHeight);
+    }
+
+    /**
+     * Show/Hide a single row/column header outline for Calc documents.
+     *
+     * @param bColumn - if we are dealing with a column or row group
+     * @param nLevel - the level to which the group belongs
+     * @param nIndex - the group entry index
+     * @param bHidden - the new group state (collapsed/expanded)
+     */
+    public func setOutlineState( column: Bool, level: Int32, index: Int32, 
hidden: Bool)
+    {
+        docClass.setOutlineState(pDoc, column, level, index, hidden);
+    }
+
+    /**
+     * Create a new view for an existing document.
+     * By default a loaded document has 1 view.
+     * @return the ID of the new view.
+     */
+    public func createView() -> Int32
+    {
+        return docClass.createView(pDoc);
+    }
+
+    /**
+     * Destroy a view of an existing document.
+     * @param nId a view ID, returned by createView().
+     */
+    public func destroyView( id: Int32 )
+    {
+        docClass.destroyView(pDoc, id);
+    }
+
+    /**
+     * Set an existing view of an existing document as current.
+     * @param nId a view ID, returned by createView().
+     */
+    public func setView(id: Int32)
+    {
+        docClass.setView(pDoc, id);
+    }
+
+    /**
+     * Get the current view.
+     * @return a view ID, previously returned by createView().
+     */
+    public func getView() -> Int32
+    {
+        return docClass.getView(pDoc);
+    }
+
+    /**
+     * Get number of views of this document.
+     */
+    public func getViewsCount() -> Int32
+    {
+        return docClass.getViewsCount(pDoc);
+    }
+
+    /**
+     * Paints a font name or character if provided to be displayed in the font 
list
+     * @param pFontName the font to be painted
+     */
+    // TODO
+//    public func renderFont(fontName: String,
+//        const char *pChar,
+//        int *pFontWidth,
+//        int *pFontHeight)
+//    {
+//        return docClass.renderFont(pDoc, pFontName, pChar, pFontWidth, 
pFontHeight);
+//    }
+
+    /**
+     * Renders a subset of the document's part to a pre-allocated buffer.
+     *
+     * @param nPart the part number of the document of which the tile is 
painted.
+     * @see paintTile.
+     */
+    public func paintPartTile(pBuffer: UnsafeMutablePointer<UInt8>,
+        nPart: Int32,
+        nCanvasWidth: Int32,
+        nCanvasHeight: Int32,
+        nTilePosX: Int32,
+        nTilePosY: Int32,
+        nTileWidth: Int32,
+        nTileHeight: Int32)
+    {
+        return docClass.paintPartTile(pDoc, pBuffer, nPart,
+            nCanvasWidth, nCanvasHeight,
+            nTilePosX, nTilePosY,
+            nTileWidth, nTileHeight);
+    }
+
+    /**
+     * Returns the viewID for each existing view. Since viewIDs are not reused,
+     * viewIDs are not the same as the index of the view in the view array over
+     * time. Use getViewsCount() to know the minimal nSize that's large enough.
+     *
+     * @param pArray the array to write the viewIDs into
+     * @param nSize the size of pArray
+     * @returns true if pArray was large enough and result is written, false
+     * otherwise.
+     */
+//    bool getViewIds(int* pArray,
+//    size_t nSize)
+//    {
+//    return docClass.getViewIds(pDoc, pArray, nSize);
+//    }
+
+    /**
+     * Set the language tag of the window with the specified nId.
+     *
+     * @param nId a view ID, returned by createView().
+     * @param language Bcp47 languageTag, like en-US or so.
+     */
+    public func setViewLanguage( id: Int32, language: String)
+    {
+        docClass.setViewLanguage(pDoc, id, language);
+    }
+
+}
+
+/**
+ * iOS friendly extensions of Document.
+ * TODO: move me back to the framework.
+ */
+public extension Document
+{
+    public func getDocumentSizeAsCGSize() -> CGSize
+    {
+        let (x,y) = self.getDocumentSize()
+        return CGSize(width: x, height: y)
+    }
+
+    public func paintTileToCurrentContext(canvasSize: CGSize,
+                                          tileRect: CGRect)
+    {
+        let ctx = UIGraphicsGetCurrentContext()
+        //print(ctx!)
+        let ptr = unsafeBitCast(ctx, to: UnsafeMutablePointer<UInt8>.self)
+        //print(ptr)
+
+        self.paintTile(pBuffer:ptr,
+                       canvasWidth: Int32(canvasSize.width),
+                       canvasHeight: Int32(canvasSize.height),
+                       tilePosX: Int32(tileRect.minX),
+                       tilePosY: Int32(tileRect.minY),
+                       tileWidth: Int32(tileRect.size.width),
+                       tileHeight: Int32(tileRect.size.height))
+    }
+
+    public func paintTileToImage(canvasSize: CGSize,
+                                 tileRect: CGRect) -> UIImage?
+    {
+        // the scaling etc here is all black magic.
+        // I don't really understand whats going on, other than that this 
combination works...
+
+        UIGraphicsBeginImageContextWithOptions(canvasSize, false, 1.0)
+        let ctx = UIGraphicsGetCurrentContext()!
+
+        //        print(ctx)
+        //        print(ctx.ctm)
+        //        print(ctx.userSpaceToDeviceSpaceTransform)
+
+        self.paintTileToCurrentContext(canvasSize: canvasSize, tileRect: 
tileRect)
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return image
+    }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
new file mode 100644
index 000000000000..34109fb88c62
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
@@ -0,0 +1,287 @@
+//
+// 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/.
+//
+
+import Foundation
+import UIKit
+
+
+
+
+/// Serves the same purpose as the LOKitThread in the Android project - 
sequentialises all access to LOKit on a background thread, off the UI thread.
+/// It's a singleton, and keeps a single instance of LibreOfficeKit
+/// Public methods may be called from any thread, and will dispatch their work 
onto the held sequential queue.
+/// TODO: move me to framework
+public class LOKitThread
+{
+    public static let instance = LOKitThread() // statics are lazy and thread 
safe in swift, so no need for anything more complex
+
+
+    fileprivate let queue = SingleThreadedQueue(name: "LOKitThread.queue")
+
+    /// singleton LibreOffice instance. Can only be accessed through the queue.
+    var libreOffice: LibreOffice! = nil // initialised in 
didFinishLaunchingWithOptions
+
+    public weak var delegate: LOKitUIDelegate? = nil
+    public weak var progressDelegate: ProgressDelegate? = nil
+
+    private init()
+    {
+
+        async {
+            self.libreOffice = try! LibreOffice() // will blow up the app if 
it throws, but fair enough
+
+            // hook up event handler
+            self.libreOffice.registerCallback(callback: self.onLOKEvent)
+
+        }
+    }
+
+    private func onLOKEvent(type: LibreOfficeKitCallbackType, payload: String?)
+    {
+        //LibreOfficeLight.LibreOfficeKitKeyEventType.
+        print("onLOKEvent type:\(type) payload:\(payload ?? "")")
+
+        switch type
+        {
+        case LOK_CALLBACK_STATUS_INDICATOR_START:
+            runOnMain {
+                self.progressDelegate?.statusIndicatorStart()
+            }
+
+        case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
+            runOnMain {
+                if let doub = Double(payload ?? "")
+                {
+                    self.progressDelegate?.statusIndicatorSetValue(value: doub)
+                }
+            }
+
+        case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
+            runOnMain {
+                self.progressDelegate?.statusIndicatorFinish()
+            }
+        default:
+             print("onLOKEvent type:\(type) not handled!")
+        }
+    }
+
+    /// Run the task on the serial queue, and return immediately
+    public func async(_ runnable: @escaping Runnable)
+    {
+        queue.async( runnable)
+    }
+
+    /// Run the task on the serial queue, and block to get the result
+    /// Careful of deadlocking!
+    public func sync<R>( _ closure: @escaping () -> R ) -> R
+    {
+        let ret = queue.sync( closure )
+        return ret
+    }
+
+    public func withLibreOffice( _ closure: @escaping (LibreOffice) -> ())
+    {
+        async {
+            closure(self.libreOffice)
+        }
+    }
+
+    /// Loads a document, and calls the callback with a wrapper if successful, 
or an error if not.
+    public func documentLoad(url: String, callback: @escaping 
(DocumentHolder?, Error?) -> ())
+    {
+        withLibreOffice
+        {
+            lo in
+
+            do
+            {
+                // this is trying to avoid null context errors which pop up on 
doc init
+                // doesnt seem to fix
+                UIGraphicsBeginImageContext(CGSize(width:1,height:1))
+                let doc = try lo.documentLoad(url: url)
+                print("Opened document: \(url)")
+                doc.initializeForRendering()
+                UIGraphicsEndImageContext()
+
+                callback(DocumentHolder(doc: doc), nil)
+            }
+            catch
+            {
+                print("Failed to load document: \(error)")
+                callback(nil, error)
+            }
+        }
+    }
+}
+
+/**
+ * Holds the document object so to enforce access in a thread safe way.
+ */
+public class DocumentHolder
+{
+    private let doc: Document
+
+    public weak var delegate: DocumentUIDelegate? = nil
+
+    init(doc: Document)
+    {
+        self.doc = doc
+        doc.registerCallback() {
+            [weak self] typ, payload in
+            self?.onDocumentEvent(type: typ, payload: payload)
+        }
+    }
+
+    /// Gives async access to the document
+    public func async(_ closure: @escaping (Document) -> ())
+    {
+        LOKitThread.instance.async
+        {
+            closure(self.doc)
+        }
+    }
+
+    /// Gives sync access to the document - blocks until the closure runs.
+    /// Careful of deadlocks.
+    public func sync<R>( _ closure: @escaping (Document) -> R ) -> R
+    {
+        return LOKitThread.instance.sync
+        {
+            return closure(self.doc)
+        }
+    }
+
+    private func onDocumentEvent(type: LibreOfficeKitCallbackType, payload: 
String?)
+    {
+        print("onDocumentEvent type:\(type) payload:\(payload ?? "")")
+
+        switch type
+        {
+        case LOK_CALLBACK_INVALIDATE_TILES:
+            runOnMain {
+                self.delegate?.invalidateTiles( rects: decodeRects(payload) )
+            }
+        case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+            runOnMain {
+                self.delegate?.invalidateVisibleCursor( rects: 
decodeRects(payload) )
+            }
+        case LOK_CALLBACK_TEXT_SELECTION:
+            runOnMain {
+                self.delegate?.textSelection( rects: decodeRects(payload) )
+            }
+        case LOK_CALLBACK_TEXT_SELECTION_START:
+            runOnMain {
+                self.delegate?.textSelectionStart( rects: decodeRects(payload) 
)
+            }
+        case LOK_CALLBACK_TEXT_SELECTION_END:
+            runOnMain {
+                self.delegate?.textSelectionEnd( rects: decodeRects(payload) )
+            }
+        default:
+            print("onDocumentEvent type:\(type) not handled!")
+        }
+    }
+
+    public func search(searchString: String, forwardDirection: Bool = true, 
from: CGPoint)
+    {
+        var rootJson = JSONObject()
+
+        addProperty(&rootJson, "SearchItem.SearchString", "string", 
searchString);
+        addProperty(&rootJson, "SearchItem.Backward", "boolean", 
String(forwardDirection) );
+        addProperty(&rootJson, "SearchItem.SearchStartPointX", "long", 
String(describing: from.x) );
+        addProperty(&rootJson, "SearchItem.SearchStartPointY", "long", 
String(describing: from.y) );
+        addProperty(&rootJson, "SearchItem.Command", "long", "1") // 
String.valueOf(0)); // search all == 1
+
+        if let jsonStr = encode(json: rootJson)
+        {
+            async {
+                $0.postUnoCommand(command: ".uno:ExecuteSearch", arguments: 
jsonStr, notifyWhenFinished: true)
+            }
+        }
+    }
+
+
+}
+
+public typealias JSONObject = Dictionary<String, AnyObject>
+public func addProperty( _ json: inout JSONObject, _ parentValue: String, _ 
type: String, _ value: String)
+{
+    var child = JSONObject();
+    child["type"] = type as AnyObject
+    child["value"] = value as AnyObject
+    json[parentValue] = child as AnyObject
+}
+
+func encode(json: JSONObject) -> String?
+{
+    //let encoder = JSONEncoder()
+
+    if let data = try? JSONSerialization.data(withJSONObject: json, options: 
.prettyPrinted)
+    {
+        return String(data: data, encoding: String.Encoding.utf8)
+    }
+    return nil
+}
+
+/// Decodes a series of rectangles in the form: "x, y, width, height; x, y, 
width, height"
+public func decodeRects(_ payload: String?) -> [CGRect]?
+{
+    guard var pl = payload else { return nil }
+    pl = pl.trimmingCharacters(in: .whitespacesAndNewlines )
+    if pl == "EMPTY" || pl.count == 0
+    {
+        return nil
+    }
+    var ret = [CGRect]()
+    for rectStr in pl.split(separator: ";")
+    {
+        let coords = rectStr.split(separator: ",").flatMap { Double($0) }
+        if coords.count == 4
+        {
+            let rect = CGRect(x: coords[0],
+                              y: coords[1],
+                              width: coords[2],
+                              height: coords[3])
+            ret.append( rect )
+        }
+    }
+    return ret
+}
+
+/**
+ * Delegate methods for global events emitted from LOKit.
+ * Mostly dispatched on the main thread unless noted.
+ */
+public protocol LOKitUIDelegate: class
+{
+    // Nothing ATM..
+}
+
+public protocol ProgressDelegate: class
+{
+    func statusIndicatorStart()
+
+    func statusIndicatorFinish()
+
+    func statusIndicatorSetValue(value: Double)
+}
+
+
+public protocol DocumentUIDelegate: class
+{
+    func invalidateTiles(rects: [CGRect]? )
+
+    func invalidateVisibleCursor(rects: [CGRect]? )
+
+    func textSelection(rects: [CGRect]? )
+    func textSelectionStart(rects: [CGRect]? )
+    func textSelectionEnd(rects: [CGRect]? )
+
+
+
+}
diff --git 
a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift
new file mode 100644
index 000000000000..de9f1ee82c2c
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift
@@ -0,0 +1,102 @@
+//
+//  LibreOfficeKitIOSTests.swift
+//  LibreOfficeKitIOSTests
+//
+//  Created by Jon Nermut on 30/12/17.
+//  Copyright © 2017 LibreOffice. All rights reserved.
+//
+
+import XCTest
+@testable import LibreOfficeKitIOS
+
+class LibreOfficeKitIOSTests: XCTestCase {
+
+    override func setUp() {
+        super.setUp()
+        // Put setup code here. This method is called before the invocation of 
each test method in the class.
+    }
+
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation 
of each test method in the class.
+        super.tearDown()
+    }
+
+
+
+    func testLoadingSimpleDoc() {
+
+        guard let lo = try? LibreOffice() else
+        {
+            XCTFail("Could not start LibreOffice")
+            return
+        }
+
+        let b = Bundle.init(for: LibreOfficeKitIOSTests.self)
+        guard let url = b.url(forResource: "test-page-format", withExtension: 
"docx") else
+        {
+            XCTFail("Failed to get url to test doc")
+            return
+        }
+
+        var loCallbackCount = 0
+        lo.registerCallback()
+        {
+            typ, payload in
+            print(typ)
+            print(payload)
+            loCallbackCount += 1
+        }
+
+        guard let doc = try? lo.documentLoad(url: url.absoluteString) else
+        {
+            XCTFail("Could not load document")
+            return
+        }
+
+        var docCallbackCount = 0
+        doc.registerCallback()
+        {
+            typ, payload in
+            print(typ)
+            print(payload)
+            docCallbackCount += 1
+        }
+
+        //let typ: LibreOfficeDocumentType = doc.getDocumentType()
+        //XCTAssertTrue(typ == LibreOfficeDocumentType.LOK_DOCTYPE_TEXT)
+
+        doc.initializeForRendering()
+        let rects = doc.getPartRectanges()
+        print(rects) // 284, 284, 12240, 15840; 284, 16408, 12240, 15840
+        let tileMode = doc.getTileMode()
+        print(tileMode) // 1
+        let canvasSize = CGSize(width: 1024,height: 1024)
+        let tile = CGRect(x: 284, y: 284, width: 12240, height: 12240)
+
+
+        guard let image = doc.paintTileToImage(canvasSize: canvasSize, 
tileRect: tile) else
+        {
+            XCTFail("No image")
+            return
+        }
+        if let data = UIImagePNGRepresentation(image)
+        {
+            let filename = 
getDocumentsDirectory().appendingPathComponent("tile1.png")
+            try? data.write(to: filename)
+            print("Wrote tile to: \(filename)")
+        }
+    }
+
+}
+
+func getDocumentsDirectory() -> URL
+{
+    let paths = FileManager.default.urls(for: .documentDirectory, in: 
.userDomainMask)
+    return paths[0]
+}
+
+public extension Document
+{
+
+}
+
diff --git 
a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift
new file mode 100644
index 000000000000..f1d6b947c8e1
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift
@@ -0,0 +1,227 @@
+//
+// 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/.
+//
+
+import Foundation
+
+
+public struct LibreOfficeError: Error
+{
+    let message: String
+    public init(_ message: String)
+    {
+        self.message = message
+    }
+}
+
+public typealias LibreOfficeCallback = (_ type: LibreOfficeKitCallbackType, _ 
payload: String?) -> ()
+
+func callbackFromLibreOffice(nType: Int32, payload: UnsafePointer<Int8>?, 
pData: UnsafeMutableRawPointer?)
+{
+    if let val = pData?.hashValue
+    {
+        if let theFunc = Callbacks.callbackRegister[val]
+        {
+            let payString = toString(payload)
+            theFunc(LibreOfficeKitCallbackType(rawValue: 
LibreOfficeKitCallbackType.RawValue(nType)), payString)
+        }
+        else
+        {
+            print("Unknown callback: \(val)")
+        }
+    }
+    else
+    {
+        print("callbackFromLibreOffice, but pData was nil")
+    }
+}
+
+
+internal struct Callbacks
+{
+    static var count = 0
+    static var callbackRegister: Dictionary<Int, LibreOfficeCallback> = [:]
+
+    static func register(callback: @escaping LibreOfficeCallback) -> Int
+    {
+        count += 1
+        let id = count
+        callbackRegister[id] = callback
+        return id
+
+    }
+}
+
+
+open class LibreOffice
+{
+    private let pLok: UnsafeMutablePointer<LibreOfficeKit>
+    private let lokClass: LibreOfficeKitClass
+
+    public init() throws
+    {
+        let b = Bundle.init(for: LibreOffice.self)
+        let path = b.bundlePath // not Bundle.main.bundlePath
+        BridgeLOkit_Init(path)
+        let pLok = BridgeLOkit_getLOK()
+        if let lokClass = pLok?.pointee.pClass?.pointee
+        {
+            self.pLok = pLok!
+            self.lokClass = lokClass
+            print("Loaded LibreOfficeKit: \(self.getVersionInfo() ?? "")")
+            return
+        }
+        throw LibreOfficeError("Unable to init LibreOfficeKit")
+    }
+
+    /**
+     * Get version information of the LOKit process
+     *
+     * @since LibreOffice 6.0
+     * @returns JSON string containing version information in format:
+     * {ProductName: <>, ProductVersion: <>, ProductExtension: <>, BuildId: <>}
+     *
+     * Eg: {"ProductName": "LibreOffice",
+     * "ProductVersion": "5.3",
+     * "ProductExtension": ".0.0.alpha0",
+     * "BuildId": "<full 40 char git hash>"}
+     */
+    public func getVersionInfo() -> String?
+    {
+        if let pRet = lokClass.getVersionInfo(pLok)
+        {
+            return String(cString: pRet) // TODO: convert JSON
+        }
+        return nil
+    }
+
+    /**
+     * Loads a document from an URL.
+     *
+     * @param pUrl the URL of the document to load
+     * @param pFilterOptions options for the import filter, e.g. SkipImages.
+     *        Another useful FilterOption is "Language=...".  It is consumed
+     *        by the documentLoad() itself, and when provided, LibreOfficeKit
+     *        switches the language accordingly first.
+     * @since pFilterOptions argument added in LibreOffice 5.0
+     */
+    public func documentLoad(url: String) throws -> Document
+    {
+        if let pDoc = lokClass.documentLoad(pLok, url)
+        {
+            return Document(pDoc: pDoc)
+        }
+        throw LibreOfficeError("Unable to load document")
+    }
+
+
+
+    /// Returns the last error as a string
+    public func getError() -> String?
+    {
+        if let cstr = lokClass.getError(pLok)
+        {
+            let ret = String(cString: cstr)
+            lokClass.freeError(cstr)
+            return ret
+        }
+        return nil
+    }
+
+
+    /**
+     * Registers a callback. LOK will invoke this function when it wants to
+     * inform the client about events.
+     *
+     * @since LibreOffice 6.0
+     * @param pCallback the callback to invoke
+     * @param pData the user data, will be passed to the callback on invocation
+     */
+    public func registerCallback( callback: @escaping LibreOfficeCallback ) -> 
Int
+    {
+        let ret = Callbacks.register(callback: callback)
+        let pointer = UnsafeMutableRawPointer(bitPattern: ret)
+        lokClass.registerCallback(pLok, callbackFromLibreOffice, pointer)
+        return ret
+    }
+
+    /**
+     * Returns details of filter types.
+     *
+     * Example returned string:
+     *
+     * {
+     *     "writer8": {
+     *         "MediaType": "application/vnd.oasis.opendocument.text"
+     *     },
+     *     "calc8": {
+     *         "MediaType": "application/vnd.oasis.opendocument.spreadsheet"
+     *     }
+     * }
+     *
+     * @since LibreOffice 6.0
+     */
+    public func getFilterTypes() -> String?
+    {
+        return toString(lokClass.getFilterTypes(pLok));
+    }
+
+    /**
+     * Set bitmask of optional features supported by the client.
+     *
+     * @since LibreOffice 6.0
+     * @see LibreOfficeKitOptionalFeatures
+     */
+    public func setOptionalFeatures(features: UInt64)
+    {
+        return lokClass.setOptionalFeatures(pLok, features);
+    }
+
+    /**
+     * Set password required for loading or editing a document.
+     *
+     * Loading the document is blocked until the password is provided.
+     *
+     * @param pURL      the URL of the document, as sent to the callback
+     * @param pPassword the password, nullptr indicates no password
+     *
+     * In response to LOK_CALLBACK_DOCUMENT_PASSWORD, a valid password
+     * will continue loading the document, an invalid password will
+     * result in another LOK_CALLBACK_DOCUMENT_PASSWORD request,
+     * and a NULL password will abort loading the document.
+     *
+     * In response to LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY, a valid
+     * password will continue loading the document, an invalid password will
+     * result in another LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY request,
+     * and a NULL password will continue loading the document in read-only
+     * mode.
+     *
+     * @since LibreOffice 6.0
+     */
+    public func setDocumentPassword(URL: String, password: String)
+    {
+        lokClass.setDocumentPassword(pLok, URL, password);
+    }
+
+
+
+
+    /**
+     * Run a macro.
+     *
+     * Same syntax as on command line is permissible (ie. the macro:// URI 
forms)
+     *
+     * @since LibreOffice 6.0
+     * @param pURL macro url to run
+     */
+
+    public func runMacro( URL: String ) -> Bool
+    {
+        return lokClass.runMacro( pLok, URL ) != 0;
+    }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift 
b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift
new file mode 100644
index 000000000000..596ca45e34a9
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift
@@ -0,0 +1,43 @@
+//
+// 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/.
+//
+
+import UIKit
+
+
+func getDocumentsDirectory() -> URL
+{
+    let paths = FileManager.default.urls(for: .documentDirectory, in: 
.userDomainMask)
+    return paths[0]
+}
+
+public extension CGRect
+{
+    public var desc: String
+    {
+        return "(x: \(self.origin.x), y: \(self.origin.y), width: 
\(self.size.width), height: \(self.size.height), maxX: \(self.maxX), maxY: 
\(self.maxY))"
+    }
+}
+
+public func toString(_ pointer: UnsafeMutablePointer<Int8>?) -> String?
+{
+    if let p = pointer
+    {
+        return String(cString: p)
+    }
+    return nil
+}
+
+public func toString(_ pointer: UnsafePointer<Int8>?) -> String?
+{
+    if let p = pointer
+    {
+        return String(cString: p)
+    }
+    return nil
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard 
b/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
index e2748dad3c8c..ccc91115c5e0 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
+++ b/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
@@ -1,11 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" 
version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" 
propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" 
colorMatched="YES" initialViewController="DGj-7d-jfR">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" 
version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" 
propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" 
colorMatched="YES" initialViewController="DGj-7d-jfR">
     <device id="ipad9_7" orientation="portrait">
         <adaptation id="fullscreen"/>
     </device>
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" 
version="13174"/>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" 
version="13772"/>
         <capability name="Constraints to layout margins" 
minToolsVersion="6.0"/>
+        <capability name="Navigation items with more than one left or right 
bar item" minToolsVersion="7.0"/>
         <capability name="documents saved in the Xcode 8 format" 
minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -20,6 +22,34 @@
                     <view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
                         <rect key="frame" x="0.0" y="0.0" width="768" 
height="1024"/>
                         <autoresizingMask key="autoresizingMask" 
flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <scrollView clipsSubviews="YES" 
multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" 
minimumZoomScale="0.5" maximumZoomScale="8" 
translatesAutoresizingMaskIntoConstraints="NO" id="cJ7-wO-9D1">
+                                <rect key="frame" x="0.0" y="64" width="768" 
height="960"/>
+                                <autoresizingMask key="autoresizingMask" 
widthSizable="YES" heightSizable="YES"/>
+                                <connections>
+                                    <outlet property="delegate" 
destination="vXZ-lx-hvc" id="mWv-AB-k2W"/>
+                                </connections>
+                            </scrollView>
+                            <view opaque="NO" userInteractionEnabled="NO" 
alpha="0.5" contentMode="scaleToFill" fixedFrame="YES" 
translatesAutoresizingMaskIntoConstraints="NO" id="URZ-zU-xtO" userLabel="Mask">
+                                <rect key="frame" x="0.0" y="64" width="768" 
height="960"/>
+                                <autoresizingMask key="autoresizingMask" 
widthSizable="YES" heightSizable="YES"/>
+                                <subviews>
+                                    <progressView opaque="NO" 
contentMode="scaleToFill" verticalHuggingPriority="750" fixedFrame="YES" 
progressViewStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" 
id="hRJ-mR-Vnv">
+                                        <rect key="frame" x="309" y="479" 
width="150" height="1.5"/>
+                                        <autoresizingMask 
key="autoresizingMask" widthSizable="YES"/>
+                                    </progressView>
+                                </subviews>
+                                <color key="backgroundColor" white="0.0" 
alpha="1" colorSpace="calibratedWhite"/>
+                            </view>
+                            <searchBar hidden="YES" contentMode="redraw" 
fixedFrame="YES" placeholder="Search for text" showsCancelButton="YES" 
translatesAutoresizingMaskIntoConstraints="NO" id="Aq0-4K-7FT">
+                                <rect key="frame" x="0.0" y="64" width="768" 
height="56"/>
+                                <autoresizingMask key="autoresizingMask" 
widthSizable="YES" flexibleMaxY="YES"/>
+                                <textInputTraits key="textInputTraits"/>
+                                <connections>
+                                    <outlet property="delegate" 
destination="vXZ-lx-hvc" id="bZ5-fa-vQ6"/>
+                                </connections>
+                            </searchBar>
+                        </subviews>
                         <color key="backgroundColor" red="1" green="1" 
blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                     </view>
                     <toolbarItems/>
@@ -29,19 +59,32 @@
                                 <action selector="doProperties:" 
destination="vXZ-lx-hvc" id="mHw-Uf-vh2"/>
                             </connections>
                         </barButtonItem>
-                        <barButtonItem key="rightBarButtonItem" 
systemItem="action" id="BNq-ol-ZVK">
-                            <connections>
-                                <segue destination="IER-X5-Ax8" 
kind="popoverPresentation" identifier="showActions" 
popoverAnchorBarButtonItem="BNq-ol-ZVK" id="xmZ-1A-ZrW">
-                                    <popoverArrowDirection 
key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
-                                </segue>
-                            </connections>
-                        </barButtonItem>
+                        <rightBarButtonItems>
+                            <barButtonItem systemItem="action" id="BNq-ol-ZVK">
+                                <connections>
+                                    <segue destination="IER-X5-Ax8" 
kind="popoverPresentation" identifier="showActions" 
popoverAnchorBarButtonItem="BNq-ol-ZVK" id="xmZ-1A-ZrW">
+                                        <popoverArrowDirection 
key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
+                                    </segue>
+                                </connections>
+                            </barButtonItem>
+                            <barButtonItem systemItem="search" id="3s2-a6-9gP">
+                                <connections>
+                                    <action selector="searchIconTapped:" 
destination="vXZ-lx-hvc" id="tSY-uz-ifH"/>
+                                </connections>
+                            </barButtonItem>
+                        </rightBarButtonItems>
                     </navigationItem>
                     <simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
+                    <connections>
+                        <outlet property="mask" destination="URZ-zU-xtO" 
id="pkw-v3-0gr"/>
+                        <outlet property="progressBar" 
destination="hRJ-mR-Vnv" id="4lJ-kG-9SW"/>
+                        <outlet property="scrollView" destination="cJ7-wO-9D1" 
id="U50-LO-plb"/>
+                        <outlet property="searchBar" destination="Aq0-4K-7FT" 
id="B3A-Ck-UdD"/>
+                    </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" 
id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="1231" y="304"/>
+            <point key="canvasLocation" x="1230.46875" y="303.515625"/>
         </scene>
         <!--Print Manager-->
         <scene sceneID="viJ-XJ-htc">
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h 
b/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
index d501ae863444..bc276e9d31e2 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
+++ b/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
@@ -10,4 +10,5 @@
 
 // LibreOfficeKit is a prelink of all used LO libraries, generated
 // as its own xCode project.
+#define LOK_USE_UNSTABLE_API
 #import "../../source/LibreOfficeKit.h"
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to