Ver código fonte

【TTS】- 功能 & UI

liujiajie 1 ano atrás
pai
commit
819dbabb16

+ 48 - 0
PDF Office/PDF Master.xcodeproj/project.pbxproj

@@ -2535,6 +2535,15 @@
 		BB24D4AA2977BE6700041659 /* KMRedactConfirmWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB24D4A62977BE6700041659 /* KMRedactConfirmWindowController.xib */; };
 		BB24D4AB2977BE6700041659 /* KMRedactConfirmWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB24D4A62977BE6700041659 /* KMRedactConfirmWindowController.xib */; };
 		BB24D4AC2977BE6700041659 /* KMRedactConfirmWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB24D4A62977BE6700041659 /* KMRedactConfirmWindowController.xib */; };
+		BB24FFDD2B28578C00A59054 /* KMTTSWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFDC2B28578C00A59054 /* KMTTSWindowController.swift */; };
+		BB24FFDE2B28578C00A59054 /* KMTTSWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFDC2B28578C00A59054 /* KMTTSWindowController.swift */; };
+		BB24FFDF2B28578C00A59054 /* KMTTSWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFDC2B28578C00A59054 /* KMTTSWindowController.swift */; };
+		BB24FFE62B2863EF00A59054 /* KMTTSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFE52B2863EF00A59054 /* KMTTSManager.swift */; };
+		BB24FFE72B2863EF00A59054 /* KMTTSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFE52B2863EF00A59054 /* KMTTSManager.swift */; };
+		BB24FFE82B2863EF00A59054 /* KMTTSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB24FFE52B2863EF00A59054 /* KMTTSManager.swift */; };
+		BB254D5A2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB254D592B2A985A00C37B3B /* KMTTSWindowController.xib */; };
+		BB254D5B2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB254D592B2A985A00C37B3B /* KMTTSWindowController.xib */; };
+		BB254D5C2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB254D592B2A985A00C37B3B /* KMTTSWindowController.xib */; };
 		BB276A4C2B0375FE00AB5578 /* KMOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFD2B132AEFAB8F0016C456 /* KMOperationQueue.swift */; };
 		BB276A4E2B03760000AB5578 /* KMOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFD2B132AEFAB8F0016C456 /* KMOperationQueue.swift */; };
 		BB276A4F2B0376A200AB5578 /* KMBatchOperateBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFD2B112AEFAAF70016C456 /* KMBatchOperateBaseViewController.swift */; };
@@ -5212,6 +5221,9 @@
 		BB1EC7FD2967B26700EC0BC3 /* KMPDFEditViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMPDFEditViewController.xib; sourceTree = "<group>"; };
 		BB24D4A52977BE6700041659 /* KMRedactConfirmWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMRedactConfirmWindowController.swift; sourceTree = "<group>"; };
 		BB24D4A62977BE6700041659 /* KMRedactConfirmWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMRedactConfirmWindowController.xib; sourceTree = "<group>"; };
+		BB24FFDC2B28578C00A59054 /* KMTTSWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMTTSWindowController.swift; sourceTree = "<group>"; };
+		BB24FFE52B2863EF00A59054 /* KMTTSManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMTTSManager.swift; sourceTree = "<group>"; };
+		BB254D592B2A985A00C37B3B /* KMTTSWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KMTTSWindowController.xib; sourceTree = "<group>"; };
 		BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOCRPDFWindowController.swift; sourceTree = "<group>"; };
 		BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KMOCRPDFWindowController.xib; sourceTree = "<group>"; };
 		BB2A98492B26A99A00647AF3 /* KMBatchAddWatermarkOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMBatchAddWatermarkOperation.swift; sourceTree = "<group>"; };
@@ -6437,6 +6449,7 @@
 		9F1F82C6292F631A0092C4B4 /* PDFTools */ = {
 			isa = PBXGroup;
 			children = (
+				BB24FFDA2B28576500A59054 /* TTS */,
 				BB1969D42B28429A00922736 /* Snapshot */,
 				ADE86A992B031FB500414DFA /* Compare */,
 				BB2F9A9E2AFC8D1100F9DD93 /* SelfSign */,
@@ -8963,6 +8976,32 @@
 			path = OCPart;
 			sourceTree = "<group>";
 		};
+		BB24FFDA2B28576500A59054 /* TTS */ = {
+			isa = PBXGroup;
+			children = (
+				BB24FFE42B285DF000A59054 /* Model */,
+				BB24FFDB2B28576E00A59054 /* WindowController */,
+			);
+			path = TTS;
+			sourceTree = "<group>";
+		};
+		BB24FFDB2B28576E00A59054 /* WindowController */ = {
+			isa = PBXGroup;
+			children = (
+				BB24FFDC2B28578C00A59054 /* KMTTSWindowController.swift */,
+				BB254D592B2A985A00C37B3B /* KMTTSWindowController.xib */,
+			);
+			path = WindowController;
+			sourceTree = "<group>";
+		};
+		BB24FFE42B285DF000A59054 /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				BB24FFE52B2863EF00A59054 /* KMTTSManager.swift */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
 		BB276A562B038CF500AB5578 /* OCRNew */ = {
 			isa = PBXGroup;
 			children = (
@@ -11211,6 +11250,7 @@
 				AD62606E2A9D968A006C6413 /* KMEditPDFAssets.xcassets in Resources */,
 				ADEC7A81299397F8009A8256 /* SF-Pro-Text-Regular.otf in Resources */,
 				BB4EEF3029763EE7003A3537 /* KMRedactBaseWindowController.xib in Resources */,
+				BB254D5A2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */,
 				BB3EAEB0293E3D6000D92407 /* KMConvertBaseWindowController.xib in Resources */,
 				AD1CA4242A061D190070541F /* KMAnnotationScreenAuthorViewItem.xib in Resources */,
 				9F705F79291A3A84005199AD /* KMHistoryFileDeleteWindowController.xib in Resources */,
@@ -11709,6 +11749,7 @@
 				ADBC2D3B299F0A5A006280C8 /* KMPrintHelpViewController.xib in Resources */,
 				ADE86AFC2B0AF5A400414DFA /* KMCompareContentSettingView.xib in Resources */,
 				BBFBE74A28DD7DDD008B2335 /* Main.storyboard in Resources */,
+				BB254D5B2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */,
 				AD8810B629A846B100178CA1 /* KMVerficationCodeWindowController.xib in Resources */,
 				BB2EDF74296ECE17003BCF58 /* KMPageEditThumbnailItem.xib in Resources */,
 				BB897232294B08DE0045787C /* KMWatermarkViewController.xib in Resources */,
@@ -12032,6 +12073,7 @@
 				ADE3C1E929A5ABC200793B13 /* KMLoginWindowController.xib in Resources */,
 				ADF6B8722A480CCE0090CB78 /* KMComparativeView.xib in Resources */,
 				AD8810B729A846B100178CA1 /* KMVerficationCodeWindowController.xib in Resources */,
+				BB254D5C2B2A985A00C37B3B /* KMTTSWindowController.xib in Resources */,
 				BBC348192955920B008D2CD1 /* KMBackgroundPropertyController.xib in Resources */,
 				9F02019B2A1F352100C9B673 /* KMAITranslationConfirmWindowController.xib in Resources */,
 				AD6260702A9D968B006C6413 /* KMEditPDFAssets.xcassets in Resources */,
@@ -12677,6 +12719,7 @@
 				BBFE6E582930724B00142C01 /* KMMergePageModel.swift in Sources */,
 				BBBF68842A3C3AF10058E14E /* NSDocumentController+KMExtension.swift in Sources */,
 				ADA9102A2A272CE2003352F0 /* KMEditPDFTextManager.swift in Sources */,
+				BB24FFE62B2863EF00A59054 /* KMTTSManager.swift in Sources */,
 				BB897229294B08720045787C /* KMWatermarkAdjectiveTopBarItemModel.swift in Sources */,
 				BB00301D298CB799002DD1A0 /* KMPreferenceManager.swift in Sources */,
 				BBF729A32B19624500576AC5 /* KMAddBackgroundOperationQueue.swift in Sources */,
@@ -12742,6 +12785,7 @@
 				BBCE57142A72713A00508EFC /* NSViewController+KMExtension.swift in Sources */,
 				8997011F28F41AB8009AF911 /* KMLeftSideViewController.swift in Sources */,
 				BB162E97295062CD0088E9D1 /* KMPageRangeTools.swift in Sources */,
+				BB24FFDD2B28578C00A59054 /* KMTTSWindowController.swift in Sources */,
 				ADD1B6EC2946C04C00C3FFF7 /* KMPrintChoosePageSizePamphletView.swift in Sources */,
 				ADBC2D11299CCD05006280C8 /* KMTextfieldButton.swift in Sources */,
 				BB7BC4DC2AD3FFC200D6BEE6 /* NSImage+ KMExtension.swift in Sources */,
@@ -13887,6 +13931,7 @@
 				9F1FE4E229406E4700E952CA /* GTMNSAnimation+Duration.m in Sources */,
 				BB49ED22293F527700C82CA2 /* KMConvertExcelSettingView.swift in Sources */,
 				BB146FF1299DC0D100784A6A /* GTLRUploadParameters.m in Sources */,
+				BB24FFE72B2863EF00A59054 /* KMTTSManager.swift in Sources */,
 				AD88108429A719D400178CA1 /* KMRegisterView.swift in Sources */,
 				BB31981F2AC57ACA00107371 /* CPDFPage+PDFListView.swift in Sources */,
 				AD3AAD5D2B0DA3D400DE5FE7 /* KMCompareTextViewItem.swift in Sources */,
@@ -13894,6 +13939,7 @@
 				BB10FAE62AFE039E00F18D65 /* KMPDFEditPageRangeWindowController.swift in Sources */,
 				9F705F77291A3A84005199AD /* KMHistoryFileDeleteWindowController.swift in Sources */,
 				BBC3482F29559E12008D2CD1 /* KMBackgroundModel.swift in Sources */,
+				BB24FFDE2B28578C00A59054 /* KMTTSWindowController.swift in Sources */,
 				BB9E2F742A495BCD000DC68D /* KMConvertSettingLimitTipView.swift in Sources */,
 				ADE3C1A029A3894900793B13 /* KMSearchTableRowView.swift in Sources */,
 				BBF8A4042AE8E10100788BAC /* KMBatchConvertParameter.swift in Sources */,
@@ -14607,6 +14653,7 @@
 				BBA00ACB2B1604D30043D903 /* KMBotaTableRowView.swift in Sources */,
 				ADBC2CFC299CA6B9006280C8 /* KMPrintDuplexPrintingSetView.swift in Sources */,
 				BBC4F9EC2AEB58290098A1A8 /* KMAlertWindowController.swift in Sources */,
+				BB24FFE82B2863EF00A59054 /* KMTTSManager.swift in Sources */,
 				BB897248294C19980045787C /* KMWatermarkAdjectiveListController.swift in Sources */,
 				9FB220F92B186C9800A5B208 /* KMAnnotationGeneralViewController.swift in Sources */,
 				BBD1F795296FE92500343885 /* KMPageEditSplitSettingView.swift in Sources */,
@@ -15154,6 +15201,7 @@
 				BBF1705A2AE296B90013CE02 /* KMView.swift in Sources */,
 				ADDEEA7C2AD3F4C800EF675D /* KMPopUpButton.swift in Sources */,
 				BB93CDE72AE757A000B29C57 /* KMToolbarItemView.swift in Sources */,
+				BB24FFDF2B28578C00A59054 /* KMTTSWindowController.swift in Sources */,
 				9F705F8F291E579F005199AD /* KMHistoryFileTableView.swift in Sources */,
 				9F0CB4812967F64D00007028 /* KMPropertiesPanelReadOnlySubVC.swift in Sources */,
 				9F0CB53B2986570600007028 /* KMDesignToken+BoxShadow.swift in Sources */,

+ 4 - 4
PDF Office/PDF Master/Class/Batch/Tools/KMBatchRemoveHeaderFooterOperation.swift

@@ -104,13 +104,13 @@ class KMBatchRemoveHeaderFooterOperation: KMBatchOperation{
         }
     }
     class func saveAsPDFRemoveAllHeaderFooterBates(_ pdfDocument: CPDFDocument, password: String?, toPath path: String, completionHandler handler: @escaping (Int) -> Void) {
-        let filePath = pdfDocument.documentURL.path
+//        let filePath = pdfDocument.documentURL.path
         let document = CPDFDocument(url: pdfDocument.documentURL)
         if let password = password {
             document?.unlock(withPassword: password)
         }
         
-        var tBates = document?.bates()
+        let tBates = document?.bates()
         tBates?.clear()
         
         let documentPath = NSTemporaryDirectory()
@@ -135,13 +135,13 @@ class KMBatchRemoveHeaderFooterOperation: KMBatchOperation{
         }
     }
     class func saveAsPDFRemoveAllHeaderFooter(_ pdfDocument: CPDFDocument, password: String?, toPath path: String, completionHandler handler: @escaping (Int) -> Void) {
-        let filePath = pdfDocument.documentURL.path
+//        let filePath = pdfDocument.documentURL.path
         let document = CPDFDocument(url: pdfDocument.documentURL)
         if let password = password {
             document?.unlock(withPassword: password)
         }
         
-        var tHeaderFooter = document?.headerFooter()
+        let tHeaderFooter = document?.headerFooter()
         tHeaderFooter?.clear()
         
         let documentPath = NSTemporaryDirectory()

+ 1 - 1
PDF Office/PDF Master/Class/Document/KMMainDocument.swift

@@ -358,7 +358,7 @@ typealias KMMainDocumentCloudUploadHanddler = (@escaping(Bool, String)->()) -> (
 //        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewDocumentChangedNotification"), object: nil)
 //        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewPageChangedNotification"), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewDidAddAnnotationNotification"), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewDidRemoveAnnotationNotification"), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.CPDFViewPageChanged, object: nil)
     }
 
     override init?(baseTabContents baseContents: CTTabContents?) {

+ 1 - 1
PDF Office/PDF Master/Class/PDFTools/AddHeaderFooter/New/View/KMHeaderFooterView.swift

@@ -527,7 +527,7 @@ class KMHeaderFooterView: KMBaseXibView, NSTextViewDelegate, NSComboBoxDelegate
     }
     
     override func addNotification() {
-        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification), name: Notification.Name("PDFViewPageChangedNotification"), object: self.pdfView)
+        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.pdfView)
         NotificationCenter.default.addObserver(self, selector: #selector(themeChanged), name: Notification.Name("AppleInterfaceThemeChangedNotification"), object: nil)
     }
     

+ 1 - 1
PDF Office/PDF Master/Class/PDFTools/AddHeaderFooter/WindowComtroller/KMHeaderFooterManagerWindowController.swift

@@ -141,7 +141,7 @@ class KMHeaderFooterManagerWindowController: NSWindowController{
         super.windowDidLoad()
         configuUI()
         loadData()
-        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification(notification:)), name: Notification.Name(rawValue: "CPDFViewPageChangedNotification"), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification(notification:)), name: NSNotification.Name.CPDFViewPageChanged, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(themeChanged(notification:)), name: Notification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)
     }
     func configuUI() { 

+ 1 - 1
PDF Office/PDF Master/Class/PDFTools/Background/New/View/KMAddBackgroundView.swift

@@ -232,7 +232,7 @@ class KMAddBackgroundView: KMBaseXibView, NSComboBoxDelegate {
     }
     
     override func addNotification() {
-        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification), name: NSNotification.Name("PDFViewPageChangedNotification"), object: self.pdfView)
+        NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.pdfView)
     }
     override func updateLanguage() {
         typeBox.title = NSLocalizedString("Source", comment: "")

+ 156 - 0
PDF Office/PDF Master/Class/PDFTools/TTS/Model/KMTTSManager.swift

@@ -0,0 +1,156 @@
+//
+//  KMTTSManager.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/12/12.
+//
+
+@objc(KMTTSManagerDelegate)
+protocol KMTTSManagerDelegate: AnyObject {
+    @objc optional func ttsMananger(_ tts: KMTTSManager, willSpeak selection: CPDFSelection)
+    @objc optional func ttsManangerDidFinishSpeech(_ tts: KMTTSManager) -> Bool
+    @objc optional func ttsManangerdidErrorSpeech(_ tts: KMTTSManager, message: String)
+}
+
+let KMTTS_SETTING_SPEECHSPEED_KEY = "TTS_Setting_SpeechSpeed_Key"
+let KMTTS_SETTING_ISCONTINUE_KEY = "TTS_Setting_IsContinue_Key"
+let KMTTS_SETTING_LANGUAGE_KEY = "TTS_Setting_Language_Key"
+
+
+import Foundation
+
+class KMTTSManager: NSObject, NSSpeechSynthesizerDelegate{
+    var delegate: KMTTSManagerDelegate?
+
+    var rate: Float {
+        get{
+            return self.speechSynthesizer.rate
+        }
+        set{
+            self.speechSynthesizer.rate = newValue
+            UserDefaults.standard.setValue(newValue, forKey: KMTTS_SETTING_SPEECHSPEED_KEY)
+            UserDefaults.standard.synchronize()
+        }
+    }
+    var isContinue = false {
+        willSet {
+           
+        }
+        didSet {
+            UserDefaults.standard.setValue(isContinue, forKey: KMTTS_SETTING_ISCONTINUE_KEY)
+            UserDefaults.standard.synchronize()
+        }
+    }
+    func isSpeaking() -> Bool {
+        return self.speechSynthesizer.isSpeaking
+    }
+    var isPaused = false
+    var speechSynthesizer: NSSpeechSynthesizer!
+
+    var speechPDFPage: CPDFPage?
+    var speechPDFSelection: CPDFSelection?
+    
+    static let defalutManager = KMTTSManager()
+    
+    override init() {
+        super.init()
+        let array = NSSpeechSynthesizer.availableVoices
+
+        var voice: NSSpeechSynthesizer.VoiceName = array.first ?? NSSpeechSynthesizer.VoiceName(rawValue: "")
+        
+        if (UserDefaults.standard.object(forKey: KMTTS_SETTING_LANGUAGE_KEY) != nil){
+            voice = UserDefaults.standard.object(forKey: KMTTS_SETTING_LANGUAGE_KEY) as! NSSpeechSynthesizer.VoiceName
+        }
+        
+        self.speechSynthesizer = NSSpeechSynthesizer(voice: voice)
+        self.speechSynthesizer.delegate = self
+        self.speechSynthesizer.usesFeedbackWindow = true
+        var rate: Float = 175
+        if let savedRate = UserDefaults.standard.object(forKey: KMTTS_SETTING_SPEECHSPEED_KEY) as? Float {
+            rate = savedRate
+        }
+        self.speechSynthesizer.rate = rate
+        
+        var isContinue = false
+        if let savedContinue = UserDefaults.standard.object(forKey: KMTTS_SETTING_ISCONTINUE_KEY) as? Bool {
+            isContinue = savedContinue
+        }
+        self.isContinue = isContinue
+    }
+    
+    func voice() -> NSSpeechSynthesizer.VoiceName {
+        return self.speechSynthesizer.voice() ?? NSSpeechSynthesizer.VoiceName(rawValue: "")
+    }
+    func setVoice(voice: NSSpeechSynthesizer.VoiceName) {
+        self.speechSynthesizer.setVoice(voice)
+        UserDefaults.standard.setValue(voice, forKey: KMTTS_SETTING_LANGUAGE_KEY)
+        UserDefaults.standard.synchronize()
+    }
+    
+    //MARK: Public
+    func startSpeakingPDFPage(_ page: CPDFPage) -> Bool {
+        guard let string = page.string else { return false }
+        guard string.count > 0 else { return false }
+        isPaused = false
+        if speechSynthesizer.isSpeaking {
+            speechSynthesizer.stopSpeaking(at: .immediateBoundary)
+        }
+        speechPDFPage = page 
+        speechPDFSelection = nil
+        return speechSynthesizer.startSpeaking(string)
+    }
+    func startSpeakingPDFSelection(_ selection: CPDFSelection) -> Bool {
+        guard let string = selection.string() else { return false }
+        guard string.count > 0 else { return false }
+        isPaused = false
+        if speechSynthesizer.isSpeaking {
+            speechSynthesizer.stopSpeaking(at: .immediateBoundary)
+        }
+        speechPDFPage = nil
+        speechPDFSelection = selection
+        return speechSynthesizer.startSpeaking(string)
+    }
+    func stopSpeaking() { 
+        if speechSynthesizer.isSpeaking || isPaused {
+            isPaused = false
+            speechSynthesizer.stopSpeaking(at: .immediateBoundary)
+        }
+    }
+    func pauseSpeaking() {
+        if speechSynthesizer.isSpeaking {
+            isPaused = true
+            speechSynthesizer.pauseSpeaking(at: .immediateBoundary)
+        }
+    }
+    func continueSpeaking() {
+        isPaused = false
+        speechSynthesizer.continueSpeaking()
+    }
+    func availableVoices() -> [NSSpeechSynthesizer.VoiceName] {
+        return NSSpeechSynthesizer.availableVoices
+    }
+    func attributesForVoice(_ voice: NSSpeechSynthesizer.VoiceName) -> [NSSpeechSynthesizer.VoiceAttributeKey : Any] {
+        return NSSpeechSynthesizer.attributes(forVoice: voice)
+    }
+    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
+        isPaused = false
+        let _ = delegate?.ttsManangerDidFinishSpeech?(self)
+    }
+    func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
+        var selection: CPDFSelection!
+        if let page = speechPDFPage {
+            if characterRange.length > 0 {
+                selection = page.selection(for: characterRange)
+            }
+        } else if let pdfSelection = speechPDFSelection {
+            selection = pdfSelection
+        }
+        delegate?.ttsMananger?(self, willSpeak: selection)
+    }
+    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterErrorAt characterIndex: Int, of string: String, message: String) {
+        delegate?.ttsManangerdidErrorSpeech?(self, message: message)
+    }
+    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterSyncMessage message: String) {
+        delegate?.ttsManangerdidErrorSpeech?(self, message: message)
+    }
+}

+ 541 - 0
PDF Office/PDF Master/Class/PDFTools/TTS/WindowController/KMTTSWindowController.swift

@@ -0,0 +1,541 @@
+//
+//  KMTTSWindowController.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/12/12.
+//
+
+import Cocoa
+
+typealias TTSCloseWindowCallback = (_ isCloseWindow: Bool) -> Void
+
+let  minSpeed: Float = 0.6
+let  maxSpeed: Float = 5.0
+let  standardSpeed: Float = 175.0
+
+class KMTTSWindowController: NSWindowController, KMTTSManagerDelegate, NSWindowDelegate, NSTextFieldDelegate{
+    var pdfView: CPDFView!
+    var closeWindowCallback: TTSCloseWindowCallback?
+    @IBOutlet var sontinuouButton: NSButton!
+    @IBOutlet var speedLabel: NSTextField!
+    @IBOutlet var languageLabel: NSTextField!
+    
+    @IBOutlet var languageComboBox: NSPopUpButton!
+    @IBOutlet var speedTextField: NSTextField!
+    @IBOutlet var speedSlider: NSSlider!
+    
+    @IBOutlet var speedStepper: NSStepper!
+    @IBOutlet var nextButton: KMToolbarItem!
+    @IBOutlet var forwardButton: KMToolbarItem!
+    
+    @IBOutlet var playButton: KMToolbarItem!
+    
+    @IBOutlet var speedBox: NSBox!
+    
+    
+    @IBOutlet var sppendCountLabel: NSTextField!
+    
+    
+    var pdfSelection: CPDFSelection?
+    
+    var currentPageIndex: Int = 0
+    
+    var isChangePage = false
+    
+    var voiceArrays = NSMutableArray()
+    
+    static let share = KMTTSWindowController()
+    
+    convenience init() {
+        self.init(windowNibName: "KMTTSWindowController")
+    }
+    
+    override func windowDidLoad() {
+        super.windowDidLoad()
+        self.speedStepper.minValue = Double(minSpeed)
+        self.speedStepper.maxValue = Double(maxSpeed);
+        self.speedSlider.minValue = Double(minSpeed);
+        self.speedSlider.maxValue = Double(maxSpeed);
+        
+        self.nextButton.toolTip = KMLocalizedString("Next Page", nil)
+//        self.nextButton.isShowCustomToolTip = true
+        self.forwardButton.toolTip = KMLocalizedString("Previous Page", nil)
+//        self.forwardButton.isShowCustomToolTip = true
+        self.speedSlider.isEnabled = true
+        self.speedStepper.isEnabled = true
+        self.playButton.toolTip = KMLocalizedString("Play", nil)
+//        self.playButton.isShowCustomToolTip = true
+        self.sppendCountLabel.stringValue = KMLocalizedString("SpeedX", nil)
+        let str = String(format: "%.1f", KMTTSManager.defalutManager.rate/Float(standardSpeed))
+        self.speedStepper.stringValue = str
+        self.speedTextField.stringValue = str
+        self.speedSlider.stringValue = str//[NSString stringWithFormat:@"%.1f",([KMTTSManager defaultManager].rate/standardSpeed)];
+        
+        self.sontinuouButton.title = KMLocalizedString("Continuous Reading", nil);
+        self.speedLabel.stringValue = KMLocalizedString("Speed", nil) + ":"//[NSString stringWithFormat:@"%@:",KMLocalizedString("Speed", nil)];
+        self.languageLabel.stringValue = KMLocalizedString("Language", nil) + ":" //[NSString stringWithFormat:@"%@:",KMLocalizedString(@"Language", nil)];
+        
+        let array = KMTTSManager.defalutManager.availableVoices()
+        let currentVoicName = KMTTSManager.defalutManager.voice()
+        var currentIndex = 0
+        let menu = NSMenu()
+        
+        for voiceType: NSSpeechSynthesizer.VoiceName in array {
+            let voiceDic = NSMutableDictionary()
+            //            let dic = KMTTSManager.defalutManager.attributesForVoice(voiceType)
+            let voiceLocaleIdentifier = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.localeIdentifier ] as! String )
+            
+            let name = self.switchLanguage(withCode: voiceLocaleIdentifier)
+            voiceDic["voiceType"] = voiceType
+            voiceDic["voiceName"] = name
+            voiceArrays.add(voiceDic)
+        }
+        voiceArrays = self.sortArray(voiceArrays)
+        for i in 0..<voiceArrays.count {
+            let voiceDic = voiceArrays[i] as! NSDictionary
+            let voiceType = voiceDic.object(forKey: "voiceType") as! NSSpeechSynthesizer.VoiceName
+            if currentVoicName == voiceType {
+                currentIndex = i
+            }
+            
+            let dic = KMTTSManager.defalutManager.attributesForVoice(voiceType) as NSDictionary
+            let code = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.localeIdentifier ] as! String )
+            var languageStaring: String? = nil
+            let voiceName = ( NSSpeechSynthesizer.attributes( forVoice: voiceType )[ NSSpeechSynthesizer.VoiceAttributeKey.name ] as! String )
+            let name = self.switchLanguage(withCode: code)
+            if voiceName.count > 0 {
+                languageStaring = "\(name)(\(voiceName))"
+            }
+            
+            let font = NSFont.systemFont(ofSize: 12.0)
+            let namefont = NSFont.systemFont(ofSize: 10.0)
+            
+            let textRowSpace = NSMutableAttributedString(string: languageStaring ?? "")
+            textRowSpace.addAttribute(.font, value: font, range: NSRange(location: 0, length: name.count))
+            textRowSpace.addAttribute(.font, value: namefont, range: NSRange(location: name.count, length: languageStaring!.count - name.count))
+            let item = NSMenuItem()
+            item.attributedTitle = textRowSpace
+            menu.addItem(item)
+        }
+        self.languageComboBox.menu = menu
+        self.languageComboBox.selectItem(at: currentIndex)
+        KMTTSManager.defalutManager.delegate = self
+        self.updateViewColor()
+        self.window?.standardWindowButton(.zoomButton)?.isHidden = true
+        if KMTTSManager.defalutManager.isContinue {
+            self.sontinuouButton.state = .on
+        } else {
+            self.sontinuouButton.state = .off
+        }
+        DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil)
+       
+        NotificationCenter.default.addObserver(self, selector: #selector(handlePageChangedNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.pdfView)
+    }
+    
+    func updateViewColor() { 
+        var viewBackcolor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)
+        var textColor = NSColor(red: 51.0/255.0, green: 51.0/255.0, blue: 51.0/255.0, alpha: 1.0)
+        var contentColor = NSColor(red: 102.0/255.0, green: 102.0/255.0, blue: 102.0/255.0, alpha: 1.0)
+        var fillColor = NSColor.white
+        if #available(macOS 10.14, *) {
+            let appearanceName = NSApp.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua])
+            if appearanceName == .darkAqua {
+                viewBackcolor = NSColor(red: 69.0/255.0, green: 69.0/255.0, blue: 71.0/255.0, alpha: 1.0)
+                textColor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)
+                contentColor = NSColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 0.6)
+                fillColor = NSColor(red: 101.0/255.0, green: 101.0/255.0, blue: 101.0/255.0, alpha: 1.0)
+            }
+        }
+        
+        self.window?.backgroundColor = viewBackcolor
+        self.window?.isMovableByWindowBackground = true
+        self.languageLabel.textColor = contentColor
+        self.speedLabel.textColor = contentColor
+        self.speedTextField.textColor = textColor
+        self.speedBox.fillColor = fillColor
+        self.sontinuouButton.setTitleColor(contentColor)
+    }
+    
+    
+    @objc func themeChanged(notification: NSNotification) {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+            self.updateViewColor()
+        }
+    }
+    @objc func handlePageChangedNotification(notification: NSNotification) {
+        guard let PDFView = notification.object as? CPDFView else { return }
+        if PDFView == self.pdfView {
+            if self.pdfView.canGoToNextPage() {
+                self.nextButton.isEnabled = true
+            } else{
+                self.nextButton.isEnabled = false
+            }
+            if self.pdfView.canGoToPreviousPage() {
+                self.forwardButton.isEnabled = true
+            } else {
+                self.forwardButton.isEnabled = false
+            }
+        }
+    }
+    func sortArray(_ array: NSArray) -> NSMutableArray {
+        let sortDesc = [NSSortDescriptor(key: "voiceName", ascending: true)]
+        let sortedArr = array.sortedArray(using: sortDesc) as NSArray
+        let tArray = NSMutableArray(array: sortedArr)
+        return tArray
+    }
+    
+    func windowShouldClose(_ sender: NSWindow) -> Bool {
+        self.stopSpeaking()
+        if let callBlack = closeWindowCallback {
+            callBlack(true)
+        }
+        return true
+    }
+    
+    override func showWindow(_ sender: Any?) {
+        super.showWindow(sender)
+        self.isChangePage = false
+        if self.pdfView.canGoToNextPage() {
+            self.nextButton.isEnabled = true
+        } else{
+            self.nextButton.isEnabled = false
+        }
+        if self.pdfView.canGoToPreviousPage() {
+            self.forwardButton.isEnabled = true
+        } else {
+            self.forwardButton.isEnabled = false
+        }
+    }
+    
+    func stopSpeaking() {
+        if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
+            self.isChangePage = true
+            KMTTSManager.defalutManager.stopSpeaking()
+            self.speedSlider.isEnabled = true
+            self.speedStepper.isEnabled = true
+            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+            self.playButton.toolTip = NSLocalizedString("Play", comment: "")
+            self.pdfView.setHighlightedSelections([])
+        }
+        self.pdfSelection = nil
+        self.pdfView = nil
+    }
+    
+    func quikeStartSpeakingPDFPage(_ page: CPDFPage) {
+        if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
+            KMTTSManager.defalutManager.stopSpeaking()
+            self.isChangePage = true
+        }
+        self.currentPageIndex = Int(self.pdfView.document?.index(for: page) ?? 0)
+        self.startSpeakingPDFPage(page)
+    }
+    func startSpeakingPDFPage(_ page: CPDFPage) {
+        let dex = self.languageComboBox.indexOfSelectedItem
+        if dex >= 0 && dex < self.voiceArrays.count {
+            let dic: NSMutableDictionary = self.voiceArrays[dex] as! NSMutableDictionary
+            if let voiceName = dic["voiceType"] {
+                KMTTSManager.defalutManager.setVoice(voice: voiceName as! NSSpeechSynthesizer.VoiceName)
+            }
+        }
+        KMTTSManager.defalutManager.rate = self.speedSlider.floatValue * Float(standardSpeed)
+        let isSuccess = KMTTSManager.defalutManager.startSpeakingPDFPage(page)
+        if isSuccess {
+            self.playButton.image = NSImage(named: "KMImageNameTTSPause")
+            self.playButton.toolTip = NSLocalizedString("Pause", comment: "")
+            self.speedSlider.isEnabled = false
+            self.speedStepper.isEnabled = false
+        } else {
+            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+            self.playButton.toolTip = NSLocalizedString("Play", comment: "")
+            self.speedSlider.isEnabled = true
+            self.speedStepper.isEnabled = true
+        }
+    }
+    func startSpeakingPDFSelection(_ selection: CPDFSelection) {
+        if KMTTSManager.defalutManager.isSpeaking() || KMTTSManager.defalutManager.isPaused {
+            KMTTSManager.defalutManager.stopSpeaking()
+            self.isChangePage = true
+        }
+        self.pdfSelection = selection
+        self.pdfView?.setCurrentSelection(nil, animate: false)
+        let dex = self.languageComboBox.indexOfSelectedItem
+        if dex >= 0 && dex < self.voiceArrays.count {
+            let dic: NSMutableDictionary = self.voiceArrays[dex] as! NSMutableDictionary
+            if let voiceName = dic["voiceType"] as? NSSpeechSynthesizer.VoiceName {
+                KMTTSManager.defalutManager.setVoice(voice: voiceName)
+            }
+        }
+        KMTTSManager.defalutManager.rate = self.speedSlider.floatValue * Float(standardSpeed)
+        let isSuccess = KMTTSManager.defalutManager.startSpeakingPDFSelection(selection)
+        if isSuccess {
+            self.speedSlider.isEnabled = false
+            self.speedStepper.isEnabled = false
+            self.playButton.image = NSImage(named: "KMImageNameTTSPause")
+            self.playButton.toolTip = NSLocalizedString("Pause", comment: "")
+        } else {
+            self.speedSlider.isEnabled = true
+            self.speedStepper.isEnabled = true
+            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+            self.playButton.toolTip = NSLocalizedString("Play", comment: "")
+        }
+    }
+    @IBAction func buttonItemClick_Next(_ sender: NSButton) {
+        if (KMTTSManager.defalutManager.isSpeaking()) {
+            var currentPageNum = self.currentPageIndex;
+            var page: CPDFPage?
+            if (currentPageNum + 1 < self.pdfView.document.pageCount) {
+                page = self.pdfView.document.page(at: UInt(currentPageNum+1))
+                self.currentPageIndex  = currentPageNum + 1;
+                
+            } else {
+                page = self.pdfView.document.page(at: self.pdfView.document.pageCount - 1)
+                self.currentPageIndex = Int(self.pdfView.document.pageCount - 1)
+            }
+            self.pdfView.go(to: page)
+            self.isChangePage = true
+            self.startSpeakingPDFPage(page!)
+        }
+    }
+    @IBAction func buttonItemClick_Forward(_ sender: NSButton) {
+        if (KMTTSManager.defalutManager.isSpeaking()) {
+            var currentPageNum = self.currentPageIndex;
+            var page: CPDFPage?
+            if (currentPageNum - 1 < 0) {
+                page = self.pdfView.document.page(at: 0)
+                self.currentPageIndex  = 0;
+                
+            } else {
+                page = self.pdfView.document.page(at: UInt(currentPageNum - 1))
+                self.currentPageIndex = currentPageNum - 1
+            }
+            self.pdfView.go(to: page)
+            self.isChangePage = true
+            self.startSpeakingPDFPage(page!)
+        }
+    }
+    @IBAction func buttonItemClick_Play(_ sender: NSButton) {
+        if (KMTTSManager.defalutManager.isPaused) {
+            self.playButton.image = NSImage(named: "KMImageNameTTSPause")
+            self.playButton.toolTip = KMLocalizedString("Pause", nil)
+            self.speedSlider.isEnabled = false
+            self.speedStepper.isEnabled = false
+            KMTTSManager.defalutManager.continueSpeaking()
+        } else if (KMTTSManager.defalutManager.isSpeaking()) {
+            self.isChangePage = true
+            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+            self.playButton.toolTip = KMLocalizedString("Play", nil)
+
+            self.speedSlider.isEnabled = true
+            self.speedStepper.isEnabled = true
+            self.pdfView.setHighlightedSelection(nil, animated: false)
+            KMTTSManager.defalutManager.pauseSpeaking()
+        } else {
+            if ((self.pdfSelection) != nil) {
+                self.startSpeakingPDFSelection(self.pdfSelection!)
+            } else {
+                let page = self.pdfView.currentPage()
+                self.currentPageIndex = self.pdfView.currentPageIndex
+                self.startSpeakingPDFPage(page!)
+            }
+        }
+    }
+    @IBAction func buttonItemClick_Continue(_ sender: NSButton) {
+        KMTTSManager.defalutManager.isContinue = !(KMTTSManager.defalutManager.isContinue)
+    }
+    @IBAction func stepperItemClick_Speed(_ sender: NSButton) {
+        var rate = self.speedStepper.floatValue
+        let str = String(format: "%.1f", rate)
+        self.speedStepper.stringValue = str
+        self.speedTextField.stringValue = str
+        self.speedSlider.stringValue = str
+        rate = Float(standardSpeed) * rate
+        KMTTSManager.defalutManager.rate = rate
+    }
+    @IBAction func sliderItemClick_Speed(_ sender: NSButton) {
+        var rate: CGFloat = CGFloat(self.speedSlider.floatValue)
+        self.speedStepper.stringValue = "\(rate)"
+        self.speedTextField.stringValue = "\(rate)"
+        self.speedSlider.stringValue = "\(rate)"
+        rate = CGFloat(standardSpeed) * rate
+        KMTTSManager.defalutManager.rate = Float(rate)
+    }
+    @IBAction func buttonItemClick_Language(_ sender: NSButton) {
+        if KMTTSManager.defalutManager.isSpeaking() {
+            self.isChangePage = true
+            if let pdfSelection = self.pdfSelection {
+                self.startSpeakingPDFSelection(pdfSelection)
+            } else {
+                let currentPageInd = self.currentPageIndex
+                if currentPageInd < (self.pdfView.document.pageCount - 1) {
+                    let page = self.pdfView.document.page(at: UInt(currentPageInd))
+                    self.startSpeakingPDFPage(page!)
+                }
+            }
+        }
+    }
+    override func mouseDown(with event: NSEvent) {
+        super.mouseDown(with: event)
+        self.window?.makeFirstResponder(self)
+    }
+    //MARK: KMTTSManagerDelegate
+    func ttsMananger(_ tts: KMTTSManager, willSpeak selection: CPDFSelection) {
+//        if (selection != nil) {
+            self.pdfView.setHighlightedSelection(selection, animated: false)
+//        }
+    }
+    func ttsManangerDidFinishSpeech(_ tts: KMTTSManager) -> Bool {
+        var isFinish = true
+        if self.pdfSelection != nil {
+            self.pdfSelection = nil
+            self.pdfView.setHighlightedSelections(nil)
+            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+            self.speedSlider.isEnabled = true
+            self.speedStepper.isEnabled = true
+            self.playButton.toolTip = KMLocalizedString("Play", nil)
+        } else {
+            if KMTTSManager.defalutManager.isContinue {
+                if !isChangePage {
+                    if let pdfSelection = self.pdfSelection {
+                        self.pdfView.setHighlightedSelections(nil)
+                        self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+                        self.speedSlider.isEnabled = true
+                        self.speedStepper.isEnabled = true
+                        self.playButton.toolTip = KMLocalizedString("Play", nil)
+                    } else {
+                        let currentPageIndex = self.currentPageIndex
+                        if currentPageIndex + 1 < self.pdfView.document.pageCount {
+                            let page = self.pdfView.document.page(at: UInt(currentPageIndex+1))
+                            self.currentPageIndex = currentPageIndex+1
+                            self.pdfView.go(to: page)
+                            self.startSpeakingPDFPage(page!)
+                            isFinish = false
+                        } else {
+                            self.pdfView.setHighlightedSelections(nil)
+                            self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+                            self.speedSlider.isEnabled = true
+                            self.speedStepper.isEnabled = true
+                            self.playButton.toolTip = KMLocalizedString("Play", nil)
+                        }
+                    }
+                }
+            } else {
+                if !isChangePage {
+                    self.speedSlider.isEnabled = true
+                    self.speedStepper.isEnabled = true
+                    self.pdfView.setHighlightedSelections(nil)
+                    self.playButton.image = NSImage(named: "KMImageNameTTSStop")
+                    self.playButton.toolTip = KMLocalizedString("Play", nil)
+                }
+            }
+            if isChangePage {
+                isChangePage = false
+            }
+        }
+        return isFinish
+    }
+    func ttsManangerdidErrorSpeech(_ tts: KMTTSManager, message: String) {
+        
+    }
+    //MARK: NSTextFieldDelegate
+    func controlTextDidEndEditing(_ obj: Notification) {
+        if let textField = obj.object as? NSTextField {
+            if textField == self.speedTextField {
+                var rate: Float = Float(textField.stringValue) ?? 0
+                if maxSpeed - rate < 0 {
+                    rate = maxSpeed
+                } else if rate - minSpeed < 0 {
+                    rate = minSpeed
+                }
+                self.speedStepper.stringValue = String(format: "%.1f", rate)
+                self.speedTextField.stringValue = String(format: "%.1f", rate)
+                self.speedSlider.stringValue = String(format: "%.1f", rate)
+                rate = standardSpeed * rate
+                KMTTSManager.defalutManager.rate = rate
+            }
+        }
+    }
+    
+    func switchLanguage(withCode code: String) -> String {
+        var language = code
+        if code == "ar_SA" {
+            language = "Arabic (Saudi Arabia)"
+        } else if code == "cs_CZ" {
+            language = "Czech (Czech republic)"
+        } else if code == "da_DK" {
+            language = "Danish (Denmark)"
+        } else if code == "de_DE" {
+            language = "German (Germany)"
+        } else if code == "el_GR" {
+            language = "Greek (Greece)"
+        } else if code == "en_AU" {
+            language = "English (Australia)"
+        } else if code == "en_GB" {
+            language = "English (UK)"
+        } else if code == "en_IE" {
+            language = "English (Ireland)"
+        } else if code == "en_US" {
+            language = "English"
+        } else if code == "en_ZA" {
+            language = "English (South Africa)"
+        } else if code == "es_ES" {
+            language = "Spanish (Spain)"
+        } else if code == "es_MX" {
+            language = "Spanish (Mexico)"
+        } else if code == "fi_FI" {
+            language = "Finnish (Finland)"
+        } else if code == "fr_CA" {
+            language = "French (Canada)"
+        } else if code == "fr_FR" {
+            language = "French (France)"
+        } else if code == "he_IL" {
+            language = "Hebrew"
+        } else if code == "hi_IN" {
+            language = "Hindi (India)"
+        } else if code == "hu_HU" {
+            language = "Hungarian (Hungary)"
+        } else if code == "id_ID" {
+            language = "Indonesian (Indonesia)"
+        } else if code == "it_IT" {
+            language = "Italian (Italy)"
+        } else if code == "ja_JP" {
+            language = "日本語"
+        } else if code == "ko_KR" {
+            language = "Korean (South Korea)"
+        } else if code == "nl_BE" {
+            language = "Dutch (Belgium)"
+        } else if code == "nl_NL" {
+            language = "Dutch (Holland)"
+        } else if code == "nb_NO" {
+            language = "Norwegian (Norway)"
+        } else if code == "pl_PL" {
+            language = "Polish (Poland)"
+        } else if code == "pt_BR" {
+            language = "Portuguese (Brazil)"
+        } else if code == "pt_PT" {
+            language = "Portuguese (Portugal)"
+        } else if code == "ro_RO" {
+            language = "Romanian (Romania)"
+        } else if code == "ru_RU" {
+            language = "Russian (Russia)"
+        } else if code == "sk_SK" {
+            language = "Slovakia (Slovakia)"
+        } else if code == "sv_SE" {
+            language = "Swe (Sweden)"
+        } else if code == "th_TH" {
+            language = "Thai (Thailand)"
+        } else if code == "tr_TR" {
+            language = "Turkish (Turkey)"
+        } else if code == "zh_CN" {
+            language = "简体中文"
+        } else if code == "zh_HK" {
+            language = "繁體中文 (香港)"
+        } else if code == "zh_TW" {
+            language = "繁體中文 (台灣)"
+        } else if code == "en_IN" {
+            language = "English"
+        }
+        return language
+    }
+}
+
+

+ 232 - 0
PDF Office/PDF Master/Class/PDFTools/TTS/WindowController/KMTTSWindowController.xib

@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="KMTTSWindowController" customModule="PDF_Master" customModuleProvider="target">
+            <connections>
+                <outlet property="forwardButton" destination="e4k-fY-QVV" id="8tb-RI-1tB"/>
+                <outlet property="languageComboBox" destination="9fT-vD-o2j" id="eih-GL-lW6"/>
+                <outlet property="languageLabel" destination="rQn-eL-pyW" id="pRa-hs-J9v"/>
+                <outlet property="nextButton" destination="iPi-uv-9TU" id="mSj-CW-N6w"/>
+                <outlet property="playButton" destination="J22-av-VRD" id="4oE-w7-5hv"/>
+                <outlet property="sontinuouButton" destination="upy-4H-SEZ" id="uUc-dp-Nec"/>
+                <outlet property="speedBox" destination="GzC-vA-NHb" id="Xg1-ek-g1t"/>
+                <outlet property="speedLabel" destination="DFJ-Ak-XZz" id="uHf-ld-u9F"/>
+                <outlet property="speedSlider" destination="kuF-Om-R8M" id="eXe-gW-nfF"/>
+                <outlet property="speedStepper" destination="rNV-Re-6ql" id="JhG-B2-bmV"/>
+                <outlet property="speedTextField" destination="xA2-tf-ljd" id="OKg-mA-fnP"/>
+                <outlet property="sppendCountLabel" destination="zCs-0F-iqc" id="zpp-Oz-cyj"/>
+                <outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <window title="TTS" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
+            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" fullSizeContentView="YES"/>
+            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+            <rect key="contentRect" x="449" y="545" width="353" height="179"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
+            <value key="minSize" type="size" width="353" height="179"/>
+            <value key="maxSize" type="size" width="353" height="179"/>
+            <view key="contentView" id="se5-gp-TjO">
+                <rect key="frame" x="0.0" y="0.0" width="353" height="187"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <button focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="e4k-fY-QVV" customClass="KMToolbarItem" customModule="PDF_Master" customModuleProvider="target">
+                        <rect key="frame" x="77" y="127" width="20" height="20"/>
+                        <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="KMImageNameTTSForward" imagePosition="only" alignment="center" focusRingType="none" imageScaling="proportionallyUpOrDown" inset="2" id="cZo-qa-ElH">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="Qi5-8w-VGb"/>
+                            <constraint firstAttribute="width" constant="20" id="SOJ-Eb-cZp"/>
+                        </constraints>
+                        <connections>
+                            <action selector="buttonItemClick_Forward:" target="-2" id="3sz-e1-G5W"/>
+                        </connections>
+                    </button>
+                    <button focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="J22-av-VRD" customClass="KMToolbarItem" customModule="PDF_Master" customModuleProvider="target">
+                        <rect key="frame" x="167" y="127" width="20" height="20"/>
+                        <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="KMImageNameTTSStop" imagePosition="only" alignment="center" focusRingType="none" imageScaling="proportionallyUpOrDown" inset="2" id="4wL-Pk-QMU">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="20" id="FXz-Rp-6x2"/>
+                            <constraint firstAttribute="height" constant="20" id="ohp-zV-fN2"/>
+                        </constraints>
+                        <connections>
+                            <action selector="buttonItemClick_Play:" target="-2" id="uzW-jl-ZIt"/>
+                        </connections>
+                    </button>
+                    <button focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="iPi-uv-9TU" customClass="KMToolbarItem" customModule="PDF_Master" customModuleProvider="target">
+                        <rect key="frame" x="257" y="127" width="20" height="20"/>
+                        <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="KMImageNameTTSNext" imagePosition="only" alignment="center" focusRingType="none" imageScaling="proportionallyUpOrDown" inset="2" id="gr1-LS-zDz">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="ZLR-0u-A9e"/>
+                            <constraint firstAttribute="width" constant="20" id="xgc-aG-JWR"/>
+                        </constraints>
+                        <connections>
+                            <action selector="buttonItemClick_Next:" target="-2" id="4Bg-i6-Fbd"/>
+                        </connections>
+                    </button>
+                    <box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="nS9-Bi-nrK">
+                        <rect key="frame" x="20" y="109" width="313" height="5"/>
+                    </box>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DFJ-Ak-XZz">
+                        <rect key="frame" x="18" y="49" width="41" height="15"/>
+                        <textFieldCell key="cell" lineBreakMode="clipping" title="Speed" id="VT8-Zk-oJq">
+                            <font key="font" metaFont="cellTitle"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kuF-Om-R8M">
+                        <rect key="frame" x="65" y="40" width="171" height="28"/>
+                        <sliderCell key="cell" state="on" alignment="left" minValue="1" maxValue="5" doubleValue="3" tickMarkPosition="above" sliderType="linear" id="XqB-cn-IzR"/>
+                        <connections>
+                            <action selector="sliderItemClick_Speed:" target="-2" id="JWB-Ml-1d3"/>
+                        </connections>
+                    </slider>
+                    <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rNV-Re-6ql">
+                        <rect key="frame" x="311" y="42" width="19" height="28"/>
+                        <stepperCell key="cell" continuous="YES" alignment="left" increment="0.10000000000000001" maxValue="100" doubleValue="40" id="dvc-LI-TGa"/>
+                        <connections>
+                            <action selector="stepperItemClick_Speed:" target="-2" id="hCI-F8-3Kh"/>
+                        </connections>
+                    </stepper>
+                    <box boxType="custom" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="GzC-vA-NHb">
+                        <rect key="frame" x="239" y="47" width="70" height="19"/>
+                        <view key="contentView" id="TIA-n5-N7G">
+                            <rect key="frame" x="1" y="1" width="68" height="17"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                            <subviews>
+                                <textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xA2-tf-ljd">
+                                    <rect key="frame" x="0.0" y="0.0" width="68" height="15"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="64" id="6sI-Pw-hRl"/>
+                                    </constraints>
+                                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" focusRingType="none" alignment="center" title="3" id="J9h-gr-ftV">
+                                        <font key="font" metaFont="cellTitle"/>
+                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                    </textFieldCell>
+                                    <connections>
+                                        <outlet property="delegate" destination="-2" id="i1Q-re-mrz"/>
+                                    </connections>
+                                </textField>
+                            </subviews>
+                            <constraints>
+                                <constraint firstAttribute="trailing" secondItem="xA2-tf-ljd" secondAttribute="trailing" constant="2" id="8Qp-4N-AuH"/>
+                                <constraint firstAttribute="bottom" secondItem="xA2-tf-ljd" secondAttribute="bottom" id="BsA-CV-v3r"/>
+                                <constraint firstItem="xA2-tf-ljd" firstAttribute="top" secondItem="TIA-n5-N7G" secondAttribute="top" constant="2" id="Mor-2Z-GL2"/>
+                                <constraint firstItem="xA2-tf-ljd" firstAttribute="leading" secondItem="TIA-n5-N7G" secondAttribute="leading" constant="2" id="b3V-rv-rUa"/>
+                            </constraints>
+                        </view>
+                        <color key="borderColor" red="0.67450980392156867" green="0.67450980392156867" blue="0.67450980392156867" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                        <color key="fillColor" white="0.62351190476190477" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    </box>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zCs-0F-iqc">
+                        <rect key="frame" x="327" y="49" width="13" height="15"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="15" id="Gs7-zH-2ub"/>
+                        </constraints>
+                        <textFieldCell key="cell" lineBreakMode="clipping" title="X" id="BXz-xw-Zsy">
+                            <font key="font" metaFont="cellTitle"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9fT-vD-o2j">
+                        <rect key="frame" x="83" y="72" width="254" height="25"/>
+                        <popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="EDh-Zf-0aV" id="Zfc-vb-Ldc">
+                            <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="menu"/>
+                            <menu key="menu" id="IZP-oW-2vi">
+                                <items>
+                                    <menuItem title="Item 1" state="on" id="EDh-Zf-0aV"/>
+                                    <menuItem title="Item 2" id="V92-kp-lhe"/>
+                                    <menuItem title="Item 3" id="YcL-bB-wCt"/>
+                                </items>
+                            </menu>
+                        </popUpButtonCell>
+                        <connections>
+                            <action selector="buttonItemClick_Language:" target="-2" id="2Ju-VW-oTv"/>
+                        </connections>
+                    </popUpButton>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rQn-eL-pyW">
+                        <rect key="frame" x="18" y="78" width="60" height="15"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="15" id="WJ9-I7-tbx"/>
+                        </constraints>
+                        <textFieldCell key="cell" lineBreakMode="clipping" title="Language" id="bOV-kf-mIJ">
+                            <font key="font" metaFont="cellTitle"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="upy-4H-SEZ">
+                        <rect key="frame" x="18" y="19" width="148" height="18"/>
+                        <buttonCell key="cell" type="check" title="Continuous Reading" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="d25-Vy-Zdu">
+                            <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="buttonItemClick_Continue:" target="-2" id="8wd-1L-Qmw"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="nS9-Bi-nrK" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" id="0i5-Qy-pQv"/>
+                    <constraint firstItem="J22-av-VRD" firstAttribute="leading" secondItem="e4k-fY-QVV" secondAttribute="trailing" constant="70" id="0l5-ee-XAh"/>
+                    <constraint firstItem="iPi-uv-9TU" firstAttribute="top" secondItem="e4k-fY-QVV" secondAttribute="top" id="2vT-iX-agP"/>
+                    <constraint firstAttribute="bottom" secondItem="upy-4H-SEZ" secondAttribute="bottom" constant="20" id="3zh-1J-ru4"/>
+                    <constraint firstItem="9fT-vD-o2j" firstAttribute="top" secondItem="nS9-Bi-nrK" secondAttribute="bottom" constant="15" id="6I1-I0-WZA"/>
+                    <constraint firstItem="upy-4H-SEZ" firstAttribute="top" secondItem="kuF-Om-R8M" secondAttribute="bottom" constant="10" id="7Jr-Ow-9WM"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="top" secondItem="9fT-vD-o2j" secondAttribute="bottom" constant="10" id="7Rq-mU-Lzk"/>
+                    <constraint firstItem="9fT-vD-o2j" firstAttribute="leading" secondItem="rQn-eL-pyW" secondAttribute="trailing" constant="10" id="AKE-VC-LbC"/>
+                    <constraint firstItem="DFJ-Ak-XZz" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" id="F75-a3-ihc"/>
+                    <constraint firstItem="J22-av-VRD" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="OFG-7x-2uD"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="centerY" secondItem="DFJ-Ak-XZz" secondAttribute="centerY" id="Ooo-k3-y2c"/>
+                    <constraint firstItem="e4k-fY-QVV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="40" id="Oz7-WR-sgx"/>
+                    <constraint firstItem="iPi-uv-9TU" firstAttribute="leading" secondItem="J22-av-VRD" secondAttribute="trailing" constant="70" id="PmI-tB-UoK"/>
+                    <constraint firstItem="9fT-vD-o2j" firstAttribute="centerY" secondItem="rQn-eL-pyW" secondAttribute="centerY" id="PqP-A9-Ile"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="leading" secondItem="kuF-Om-R8M" secondAttribute="trailing" constant="5" id="Tur-QR-UpG"/>
+                    <constraint firstItem="rQn-eL-pyW" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" id="WU1-N0-ihR"/>
+                    <constraint firstItem="nS9-Bi-nrK" firstAttribute="top" secondItem="e4k-fY-QVV" secondAttribute="bottom" constant="15" id="Wtd-Op-cFs"/>
+                    <constraint firstItem="kuF-Om-R8M" firstAttribute="leading" secondItem="DFJ-Ak-XZz" secondAttribute="trailing" constant="10" id="eZ5-HO-kUa"/>
+                    <constraint firstItem="rNV-Re-6ql" firstAttribute="leading" secondItem="GzC-vA-NHb" secondAttribute="trailing" constant="5" id="ftf-KQ-b6l"/>
+                    <constraint firstItem="upy-4H-SEZ" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" id="iTX-Ih-n14"/>
+                    <constraint firstItem="zCs-0F-iqc" firstAttribute="leading" secondItem="rNV-Re-6ql" secondAttribute="trailing" constant="2" id="kDv-Yj-zEI"/>
+                    <constraint firstAttribute="trailing" secondItem="nS9-Bi-nrK" secondAttribute="trailing" constant="20" id="pQh-va-Pjg"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="centerY" secondItem="DFJ-Ak-XZz" secondAttribute="centerY" id="q95-01-Gya"/>
+                    <constraint firstAttribute="trailing" secondItem="9fT-vD-o2j" secondAttribute="trailing" constant="20" id="qdK-nl-QB4"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="centerY" secondItem="kuF-Om-R8M" secondAttribute="centerY" id="r4R-Ps-N0D"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="centerY" secondItem="zCs-0F-iqc" secondAttribute="centerY" id="rrd-VG-uF1"/>
+                    <constraint firstItem="J22-av-VRD" firstAttribute="top" secondItem="e4k-fY-QVV" secondAttribute="top" id="ry0-EP-b9Z"/>
+                    <constraint firstItem="J22-av-VRD" firstAttribute="bottom" secondItem="e4k-fY-QVV" secondAttribute="bottom" id="tSW-vg-ftW"/>
+                    <constraint firstItem="GzC-vA-NHb" firstAttribute="centerY" secondItem="rNV-Re-6ql" secondAttribute="centerY" id="tmn-cB-06s"/>
+                    <constraint firstItem="iPi-uv-9TU" firstAttribute="bottom" secondItem="e4k-fY-QVV" secondAttribute="bottom" id="uwP-Os-s7h"/>
+                    <constraint firstAttribute="trailing" secondItem="zCs-0F-iqc" secondAttribute="trailing" constant="15" id="yar-1h-EzA"/>
+                </constraints>
+            </view>
+            <connections>
+                <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
+            </connections>
+            <point key="canvasLocation" x="141.5" y="161"/>
+        </window>
+    </objects>
+    <resources>
+        <image name="KMImageNameTTSForward" width="20" height="20"/>
+        <image name="KMImageNameTTSNext" width="20" height="20"/>
+        <image name="KMImageNameTTSStop" width="20" height="20"/>
+    </resources>
+</document>

+ 39 - 0
PDF Office/PDF Master/Class/PDFWindowController/ViewController/KMMainViewController+Action.swift

@@ -3600,6 +3600,7 @@ extension KMMainViewController : KMMainToolbarControllerDelegate {
                     self.listView.takeSnapshot(nil)
                 } else if toolbarItem.itemIdentifier == KMToolbarTTSIdentifier {
                     Swift.debugPrint("KMToolbarTTSIdentifier ...")
+                    self.showTTSWindow()
                 } else if toolbarItem.itemIdentifier == KMToolbarConversionOCRIdentifier {
                     self.showOCRWindow()
                 } else if toolbarItem.itemIdentifier == KMAnnotationImageToolbarItemIdentifier {
@@ -3611,6 +3612,44 @@ extension KMMainViewController : KMMainToolbarControllerDelegate {
         }
     }
     
+    func showTTSWindow() {
+        
+        var lastPDFView: CPDFView?
+        let ttsView = KMTTSWindowController.share
+        if (ttsView.window?.isVisible ?? false) {
+            lastPDFView = ttsView.pdfView
+            if lastPDFView?.document?.documentURL?.path == self.listView.document?.documentURL?.path {
+                lastPDFView = nil
+                ttsView.window?.orderOut(nil)
+            } else {
+                ttsView.pdfView = self.listView
+                ttsView.showWindow(nil)
+            }
+        } else {
+            ttsView.pdfView = self.listView
+            ttsView.showWindow(nil)
+        }
+//        weak var blockSelf = self
+        ttsView.closeWindowCallback = { (isCloseWindow: Bool) in
+            if isCloseWindow {
+//                for item in blockSelf?.toolbar.items ?? [] {
+//                    if item.itemIdentifier == KMToolbarTTSViewItemIdentifier {
+//                        item.isSelected = false
+//                    }
+//                }
+            }
+        }
+
+        if let currentSelection = self.listView.currentSelection,
+            currentSelection.selectionsByLine.count > 0 {
+            ttsView.startSpeakingPDFSelection(currentSelection)
+        }
+        if let lastPDFView = lastPDFView {
+            lastPDFView.setHighlightedSelections([])
+            ttsView.stopSpeaking()
+        }
+    }
+    
     func toolbarViewController(_ viewController: KMToolbarViewController, menuItemDidClick toolbarItem: KMToolbarItemView, index: Int, info: Any?) {
         if (toolbarItem.itemIdentifier == KMToolbarToolWatermarkItemIdentifier) {
             KMPrint("KMToolbarToolWatermarkItemIdentifier \(index)")