You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@corinthia.apache.org by pm...@apache.org on 2015/08/19 17:38:21 UTC
[4/4] incubator-corinthia git commit: Add Objective C framework code
from UX Write
Add Objective C framework code from UX Write
These files come from the portion of the UX Write editor that works on
both Apple platforms (that is, it can run on either OS X or iOS). It's
in the repository for illustrative purposes only, to assist with the
creation of the framework for the Corinthia editor UI. The code does not
compile independently in its present form.
Project: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/commit/9f851a34
Tree: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/tree/9f851a34
Diff: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/diff/9f851a34
Branch: refs/heads/master
Commit: 9f851a34268fc20fe708626dff9795f4526edee9
Parents: 5202fbf
Author: Peter Kelly <pe...@uxproductivity.com>
Authored: Wed Aug 19 22:37:11 2015 +0700
Committer: Peter Kelly <pe...@uxproductivity.com>
Committed: Wed Aug 19 22:37:11 2015 +0700
----------------------------------------------------------------------
experiments/objcFramework/DocFormats.h | 79 +
experiments/objcFramework/EDDocumentSetup.h | 32 +
experiments/objcFramework/EDDocumentSetup.m | 225 +++
experiments/objcFramework/EDEditor.h | 112 ++
experiments/objcFramework/EDEditor.m | 413 ++++
experiments/objcFramework/EDFileFormat.h | 65 +
experiments/objcFramework/EDFileFormat.m | 968 ++++++++++
experiments/objcFramework/EDFindReplace.h | 48 +
experiments/objcFramework/EDFindReplace.m | 82 +
experiments/objcFramework/EDGeometry.h | 80 +
experiments/objcFramework/EDGeometry.m | 119 ++
experiments/objcFramework/EDHTMLTidy.h | 42 +
experiments/objcFramework/EDHTMLTidy.m | 58 +
experiments/objcFramework/EDJSInterface.h | 365 ++++
experiments/objcFramework/EDJSInterface.m | 1775 ++++++++++++++++++
experiments/objcFramework/EDObservation.h | 30 +
experiments/objcFramework/EDObservation.m | 56 +
experiments/objcFramework/EDOutline.h | 132 ++
experiments/objcFramework/EDOutline.m | 270 +++
experiments/objcFramework/EDSaveOperation.h | 44 +
experiments/objcFramework/EDSaveOperation.m | 166 ++
experiments/objcFramework/EDScan.h | 64 +
experiments/objcFramework/EDScan.m | 138 ++
experiments/objcFramework/EDScanResults.h | 73 +
experiments/objcFramework/EDScanResults.m | 109 ++
.../objcFramework/EDSelectionFormatting.h | 55 +
.../objcFramework/EDSelectionFormatting.m | 73 +
experiments/objcFramework/EDStyle.h | 59 +
experiments/objcFramework/EDStyle.m | 150 ++
experiments/objcFramework/EDTiming.h | 80 +
experiments/objcFramework/EDTiming.m | 271 +++
experiments/objcFramework/EDUtil.h | 41 +
experiments/objcFramework/EDUtil.m | 325 ++++
experiments/objcFramework/EDWordCount.h | 42 +
experiments/objcFramework/EDWordCount.m | 57 +
experiments/objcFramework/Editor.h | 40 +
36 files changed, 6738 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/DocFormats.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/DocFormats.h b/experiments/objcFramework/DocFormats.h
new file mode 100644
index 0000000..ebfc49c
--- /dev/null
+++ b/experiments/objcFramework/DocFormats.h
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+/** \mainpage
+
+ Documentation for the DocFormats library
+
+ # CSS
+
+ \see CSSProperties.h
+ \see CSSSheet.h
+ \see CSSStyle.h
+
+ # XML
+
+ \see DFDOM.h
+
+ # Word
+
+ \see WordPPr.h
+
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "DFPlatform.h"
+#import <DocFormats/DocFormats.h>
+
+//#import <DocFormats/DFXMLNames.h>
+#import "DFXMLNames.h"
+//#import <DocFormats/DFXMLNamespaces.h>
+#import "DFXMLNamespaces.h"
+
+//#import <DocFormats/DFDOM.h>
+//#import <DocFormats/DFXML.h>
+#import "DFDOM.h"
+#import "DFXML.h"
+#import <DocFormats/DFXMLForward.h>
+
+#import "CSS.h"
+#import "CSSClassNames.h"
+#import "CSSLength.h"
+#import "CSSProperties.h"
+#import "CSSSelector.h"
+#import "CSSStyle.h"
+#import "CSSSheet.h"
+
+#import "DFHTML.h"
+#import "DFHTMLNormalization.h"
+
+#import "DFString.h"
+//#import "DFHashTable.h"
+#import "DFCallback.h"
+#import "DFFilesystem.h"
+#import <DocFormats/DFError.h>
+#import "DFZipFile.h"
+#import "Word.h"
+#import "HTMLToLaTeX.h"
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDDocumentSetup.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDDocumentSetup.h b/experiments/objcFramework/EDDocumentSetup.h
new file mode 100644
index 0000000..e05c2bf
--- /dev/null
+++ b/experiments/objcFramework/EDDocumentSetup.h
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import <Foundation/Foundation.h>
+#import <Editor/DocFormats.h>
+
+void CSSSheetUseCSSNumbering(CSSSheet *sheet);
+CSSSheet *CSSSheetCreateDefault(void);
+DFDocument *DFHTMLCreateDefault(CSSSheet *styleSheet);
+int DFHTMLCreateDefaultFile(const char *filename, DFError **error);
+int DFWordCreateDefault(const char *filename, DFError **error);
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDDocumentSetup.m
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDDocumentSetup.m b/experiments/objcFramework/EDDocumentSetup.m
new file mode 100644
index 0000000..1ee2283
--- /dev/null
+++ b/experiments/objcFramework/EDDocumentSetup.m
@@ -0,0 +1,225 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import "EDDocumentSetup.h"
+#import "EDUtil.h"
+#import <FileClient/FileClient.h>
+
+#define L10NDocStringPreface NSLocalizedString(@"DocStringPreface",NULL)
+#define L10NDocStringRef NSLocalizedString(@"DocStringRef",NULL)
+#define L10NDocStringAbstract NSLocalizedString(@"DocStringAbstract",NULL)
+#define L10NDocStringBib NSLocalizedString(@"DocStringBib",NULL)
+#define L10NDocStringChapter NSLocalizedString(@"DocStringChapter",NULL)
+#define L10NDocStringAppendix NSLocalizedString(@"DocStringAppendix",NULL)
+#define L10NDocStringContents NSLocalizedString(@"DocStringContents",NULL)
+#define L10NDocStringListFigure NSLocalizedString(@"DocStringListFigure",NULL)
+#define L10NDocStringListTable NSLocalizedString(@"DocStringListTable",NULL)
+#define L10NDocStringIndex NSLocalizedString(@"DocStringIndex",NULL)
+#define L10NDocStringFigure NSLocalizedString(@"DocStringFigure",NULL)
+#define L10NDocStringTable NSLocalizedString(@"DocStringTable",NULL)
+#define L10NDocStringPart NSLocalizedString(@"DocStringPart",NULL)
+#define L10NDocStringEncl NSLocalizedString(@"DocStringEncl",NULL)
+#define L10NDocStringCc NSLocalizedString(@"DocStringCc",NULL)
+#define L10NDocStringHeadto NSLocalizedString(@"DocStringHeadto",NULL)
+#define L10NDocStringPage NSLocalizedString(@"DocStringPage",NULL)
+#define L10NDocStringSee NSLocalizedString(@"DocStringSee",NULL)
+#define L10NDocStringAlso NSLocalizedString(@"DocStringAlso",NULL)
+#define L10NDocStringProof NSLocalizedString(@"DocStringProof",NULL)
+#define L10NDocStringGlossary NSLocalizedString(@"DocStringGlossary",NULL)
+
+// FIXME: This won't work now for Word documents, where styles always have class names
+// FIXME: Not covered by tests
+void CSSSheetUseCSSNumbering(CSSSheet *sheet)
+{
+ CSSStyle *style;
+
+ style = CSSSheetLookupElement(sheet,"body",NULL,1,0);
+ CSSPut(CSSStyleRule(style),"counter-reset","h1 h2 h3 h4 h5 h6 figure table");
+
+ // Figure caption
+ style = CSSSheetLookupElement(sheet,"figcaption",NULL,1,1);
+ CSSPut(CSSStyleRule(style),"counter-increment","figure");
+ NSString *content = [NSString stringWithFormat: @"\"%@ \" counter(figure) \": \"", L10NDocStringFigure];
+ CSSPut(CSSStyleBefore(style),"content",content.UTF8String);
+
+ style = CSSSheetLookupElement(sheet,"figcaption","Unnumbered",1,1);
+ CSSPut(CSSStyleRule(style),"counter-increment","figure 0");
+ CSSPut(CSSStyleBefore(style),"content","\"\"");
+
+ // Table caption
+ style = CSSSheetLookupElement(sheet,"caption",NULL,1,1);
+ CSSPut(CSSStyleRule(style),"caption-side","bottom");
+ CSSPut(CSSStyleRule(style),"counter-increment","table");
+ content = [NSString stringWithFormat: @"\"%@ \" counter(table) \": \"", L10NDocStringTable];
+ CSSPut(CSSStyleBefore(style),"content",content.UTF8String);
+
+ style = CSSSheetLookupElement(sheet,"caption","Unnumbered",1,1);
+ CSSPut(CSSStyleRule(style),"counter-increment","table 0");
+ CSSPut(CSSStyleBefore(style),"content","\"\"");
+
+ // Table of contents
+ style = CSSSheetLookupElement(sheet,"nav","tableofcontents",1,1);
+ if (CSSGet(CSSStyleBefore(style),"content") == NULL) {
+ content = [NSString stringWithFormat: @"\"%@\"", L10NDocStringContents];
+ CSSPut(CSSStyleBefore(style),"content",content.UTF8String);
+ CSSPut(CSSStyleBefore(style),"font-size","2em");
+ CSSSetBold(CSSStyleBefore(style),1);
+ CSSPut(CSSStyleBefore(style),"margin-top",".67em");
+ CSSPut(CSSStyleBefore(style),"margin-bottom",".67em");
+ CSSPut(CSSStyleBefore(style),"display","block");
+ }
+
+ // List of figures
+ style = CSSSheetLookupElement(sheet,"nav","listoffigures",1,1);
+ if (CSSGet(CSSStyleBefore(style),"content") == NULL) {
+ content = [NSString stringWithFormat: @"\"%@\"", L10NDocStringListFigure];
+ CSSPut(CSSStyleBefore(style),"content",content.UTF8String);
+ CSSPut(CSSStyleBefore(style),"font-size","2em");
+ CSSSetBold(CSSStyleBefore(style),1);
+ CSSPut(CSSStyleBefore(style),"margin-top",".67em");
+ CSSPut(CSSStyleBefore(style),"margin-bottom",".67em");
+ CSSPut(CSSStyleBefore(style),"display","block");
+ }
+
+ // List of tables
+ style = CSSSheetLookupElement(sheet,"nav","listoftables",1,1);
+ if (CSSGet(CSSStyleBefore(style),"content") == NULL) {
+ content = [NSString stringWithFormat: @"\"%@\"", L10NDocStringListTable];
+ CSSPut(CSSStyleBefore(style),"content",content.UTF8String);
+ CSSPut(CSSStyleBefore(style),"font-size","2em");
+ CSSSetBold(CSSStyleBefore(style),1);
+ CSSPut(CSSStyleBefore(style),"margin-top",".67em");
+ CSSPut(CSSStyleBefore(style),"margin-bottom",".67em");
+ CSSPut(CSSStyleBefore(style),"display","block");
+ }
+}
+
+CSSSheet *CSSSheetCreateDefault(void)
+{
+ CSSSheet *styleSheet = CSSSheetNew();
+ CSSSheetUseCSSNumbering(styleSheet);
+
+ CSSStyle *title = CSSSheetLookupElement(styleSheet,"p","Title",1,0);
+ CSSPut(CSSStyleRule(title),"font-size","24pt");
+ CSSPut(CSSStyleRule(title),"text-align","center");
+
+ CSSStyle *author = CSSSheetLookupElement(styleSheet,"p","Author",1,0);
+ CSSPut(CSSStyleRule(author),"font-size","18pt");
+ CSSPut(CSSStyleRule(author),"text-align","center");
+
+ CSSStyle *abstract = CSSSheetLookupElement(styleSheet,"p","Abstract",1,0);
+ CSSPut(CSSStyleRule(abstract),"font-style","italic");
+ CSSPut(CSSStyleRule(abstract),"margin-left","20%");
+ CSSPut(CSSStyleRule(abstract),"margin-right","20%");
+
+ CSSStyle *body = CSSSheetLookupElement(styleSheet,"body",NULL,1,0);
+ CSSPut(CSSStyleRule(body),"margin-left","10%");
+ CSSPut(CSSStyleRule(body),"margin-right","10%");
+ CSSPut(CSSStyleRule(body),"margin-top","10%");
+ CSSPut(CSSStyleRule(body),"margin-bottom","10%");
+ CSSPut(CSSStyleRule(body),"text-align","justify");
+
+ CSSStyle *page = CSSSheetLookupElement(styleSheet,"@page",NULL,1,0);
+ CSSPut(CSSStyleRule(page),"size","a4 portrait");
+ return styleSheet;
+}
+
+DFDocument *DFHTMLCreateDefault(CSSSheet *styleSheet)
+{
+ DFDocument *doc = DFDocumentNewWithRoot(HTML_HTML);
+ DFNode *head = DFCreateChildElement(doc->root,HTML_HEAD);
+ DFNode *body = DFCreateChildElement(doc->root,HTML_BODY);
+ DFNode *meta = DFCreateChildElement(head,HTML_META);
+ DFSetAttribute(meta,HTML_CHARSET,"utf-8");
+ DFNode *style = DFCreateChildElement(head,HTML_STYLE);
+ char *cssText = CSSSheetCopyCSSText(styleSheet);
+ DFCreateChildTextNode(style,cssText);
+ free(cssText);
+ DFNode *p = DFCreateChildElement(body,HTML_P);
+ DFCreateChildElement(p,HTML_BR);
+ return doc;
+}
+
+int DFHTMLCreateDefaultFile(const char *filename, DFError **error)
+{
+ CSSSheet *styleSheet = CSSSheetCreateDefault();
+ DFDocument *doc = DFHTMLCreateDefault(styleSheet);
+ int ok = DFSerializeXMLFile(doc,0,1,filename,error);
+ DFDocumentRelease(doc);
+ CSSSheetRelease(styleSheet);
+ return ok;
+}
+
+static void addDefaultStyles(CSSSheet *styleSheet)
+{
+ // Explicitly add default heading formatting properties
+ for (int i = 1; i <= 6; i++) {
+ char *elementName = DFFormatString("h%d",i);
+ CSSStyle *style = CSSSheetLookupElement(styleSheet,elementName,NULL,1,0);
+ CSSStyleAddDefaultHTMLProperties(style);
+ free(elementName);
+ }
+
+ // Word uses the same "Caption" style for both figures and tables. So we only need to set
+ // caption. Note that in HTML the alignment is (I think) implicit.
+ CSSStyle *caption = CSSSheetLookupElement(styleSheet,"caption",NULL,1,0);
+ CSSPut(CSSStyleRule(caption),"text-align","center");
+ caption->latent = 0;
+}
+
+int DFWordCreateDefault(const char *filename, DFError **error)
+{
+ int ok = 0;
+ CSSSheet *styleSheet = NULL;
+ DFDocument *htmlDoc = NULL;
+ DFStorage *abstractStorage = NULL;
+ DFConcreteDocument *concreteDoc = NULL;
+ DFAbstractDocument *abstractDoc = NULL;
+
+ styleSheet = CSSSheetCreateDefault();
+ addDefaultStyles(styleSheet);
+
+ concreteDoc = DFConcreteDocumentCreateFile(filename,error);
+ if (concreteDoc == NULL)
+ goto end;
+
+ abstractStorage = DFStorageNewMemory(DFFileFormatHTML);
+ abstractDoc = DFAbstractDocumentNew(abstractStorage);
+
+ htmlDoc = DFHTMLCreateDefault(styleSheet);
+ DFAbstractDocumentSetHTML(abstractDoc,htmlDoc);
+
+ if (!DFCreate(concreteDoc,abstractDoc,error))
+ goto end;
+
+ ok = 1;
+
+end:
+ CSSSheetRelease(styleSheet);
+ DFDocumentRelease(htmlDoc);
+ DFStorageRelease(abstractStorage);
+ DFConcreteDocumentRelease(concreteDoc);
+ DFAbstractDocumentRelease(abstractDoc);
+ return ok;
+}
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDEditor.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDEditor.h b/experiments/objcFramework/EDEditor.h
new file mode 100644
index 0000000..e3f53a7
--- /dev/null
+++ b/experiments/objcFramework/EDEditor.h
@@ -0,0 +1,112 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import <Foundation/Foundation.h>
+#import <Editor/DocFormats.h>
+#import "EDGeometry.h"
+#import "EDSaveOperation.h"
+
+@class EDFileFormat;
+@class JSInterface;
+@class EDOutline;
+@class EDSelectionFormatting;
+@class EDTimingInfo;
+@class EDSaveOperation;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDSystemDelegate //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@protocol EDSystemDelegate
+
+- (void)runInBackground:(void (^)(void))block completion:(void (^)(void))completion;
+- (void)runCommandInBackground:(BOOL (^)(NSError **commandError))command
+ completion:(void (^)(NSError *completionError))completion;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDEditorDelegate //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@protocol EDEditorDelegate
+
+@property (nonatomic) BOOL editorIsSaving;
+@property (nonatomic, readonly) NSString *editorPath;
+
+- (void)editorDidUpdateCSS;
+- (void)editorDidUpdateOutline;
+- (void)editorDidSaveFile;
+- (void)editorShowResizeHandles:(EDItemGeometry *)geometry vertical:(BOOL)vertical;
+- (void)editorHideResizeHandles;
+
+- (NSString *)saveResource:(NSData *)data prefix:(NSString *)prefix extension:(NSString *)extension
+ error:(NSError **)error;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDEditor //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface EDEditor : NSObject <EDResizeDelegate>
+
+@property (weak) NSObject<EDEditorDelegate> *delegate;
+@property (weak) NSObject<EDSystemDelegate> *system;
+@property (strong, readonly) EDFileFormat *fileFormat;
+@property (strong, readonly) NSString *generator;
+@property (strong, readonly) NSString *tempDir;
+@property (strong, readwrite) JSInterface *js;
+@property (assign, readonly) BOOL jsInitOk;
+@property (strong, readonly) EDOutline *outline;
+@property (strong, readwrite, nonatomic) EDSelectionFormatting *formatting;
+@property (copy, nonatomic) NSString *paragraphStyleId;
+@property (assign, readonly) CSSSheet *styleSheet;
+@property (strong, readonly) EDTimingInfo *loadTiming;
+@property (strong, readonly) EDTimingInfo *saveTiming;
+@property (strong) EDSaveOperation *activeSave;
+@property (strong) EDSaveOperation *pendingSave;
+@property (strong, readonly) NSString *origGenerator;
+@property (copy, nonatomic) NSString *locale;
+
+- (EDEditor *)initWithFileFormat:(EDFileFormat *)fileFormat generator:(NSString *)generator tempDir:(NSString *)tempDir;
+- (void)updateFormatting;
+- (void)updateCSS;
+- (void)retrieveStyles;
+- (void)makeStyleNonLatent:(const char *)ident;
+- (void)setOutlineDirty;
+- (void)loadAndInitSucceeded;
+- (void)dumpHTML;
+- (void)saveTo:(NSString *)path completion:(EDSaveCompletion)completion;
+- (void)debugSaveStatus;
+- (void)undo;
+- (void)redo;
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDEditor.m
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDEditor.m b/experiments/objcFramework/EDEditor.m
new file mode 100644
index 0000000..d653952
--- /dev/null
+++ b/experiments/objcFramework/EDEditor.m
@@ -0,0 +1,413 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import "EDEditor.h"
+#import "EDOutline.h"
+#import "EDSelectionFormatting.h"
+#import "EDTiming.h"
+#import "EDObservation.h"
+#import "EDJSInterface.h"
+#import "EDSaveOperation.h"
+#import "EDFileFormat.h"
+#import "EDHTMLTidy.h"
+#import "EDUtil.h"
+#import "EDDocumentSetup.h"
+#import <FileClient/FCError.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDEditorDelegate //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation EDEditor
+{
+ BOOL _outlineDirty;
+ int _programmaticChange;
+ BOOL _formattingDirty;
+ NSDictionary *_lastReceivedFormatting;
+}
+
+static void formattingPropertiesChanged(void *ctx, void *object, void *data)
+{
+ EDEditor *editor = (__bridge EDEditor *)ctx;
+ [editor detectedFormattingChange];
+}
+
+- (EDEditor *)initWithFileFormat:(EDFileFormat *)fileFormat generator:(NSString *)generator tempDir:(NSString *)tempDir
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _fileFormat = fileFormat;
+ _fileFormat.editor = self;
+ _generator = [generator copy];
+ _tempDir = [tempDir copy];
+ _outline = [[EDOutline alloc] init];
+ _styleSheet = CSSSheetNew();
+ _loadTiming = [[EDTimingInfo alloc] init];
+ _saveTiming = [[EDTimingInfo alloc] init];
+
+ self.formatting = [[EDSelectionFormatting alloc] init];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ self.formatting = nil;
+ CSSSheetRelease(_styleSheet);
+}
+
+- (void)setFormatting:(EDSelectionFormatting *)newFormatting
+{
+ if (_formatting != nil) {
+ removeObserverForAllProperties(_formatting,self);
+ DFCallbackRemove(&_formatting.cssProperties->changeCallbacks,formattingPropertiesChanged,(__bridge void *)self);
+ }
+
+ _formatting = newFormatting;
+
+ if (_formatting != nil) {
+ addObserverForAllProperties(_formatting,self,0,nil);
+ DFCallbackAdd(&_formatting.cssProperties->changeCallbacks,formattingPropertiesChanged,(__bridge void *)self);
+ }
+}
+
+- (void)setParagraphStyleId:(NSString *)paragraphStyleId
+{
+ _paragraphStyleId = [paragraphStyleId copy];
+ [self detectedFormattingChange];
+}
+
+- (void)setLocale:(NSString *)newLocale
+{
+ _locale = [newLocale copy];
+ [_js.main setLanguage: _locale];
+}
+
+- (void)updateResizeHandles
+{
+ BOOL vertical = NO;
+ EDItemGeometry *geometry = nil;
+ if (_formatting.inFigure) {
+ NSString *itemId = [_js.figures getSelectedFigureId];
+ if (itemId != nil) {
+ NSDictionary *dict = [_js.figures getGeometry: itemId];
+ if (dict != nil) {
+ geometry = [EDItemGeometry fromDict: dict];
+ vertical = YES;
+ }
+ }
+ }
+ else if (_formatting.inTable) {
+ NSString *itemId = [_js.tables getSelectedTableId];
+ if (itemId != nil) {
+ NSDictionary *dict = [_js.tables getGeometry: itemId];
+ if (dict != nil) {
+ geometry = [EDItemGeometry fromDict: dict];
+ vertical = NO;
+ }
+ }
+ }
+
+ if (geometry != nil)
+ [_delegate editorShowResizeHandles: geometry vertical: vertical];
+ else
+ [_delegate editorHideResizeHandles];
+}
+
+- (void)updateFormatting
+{
+ if (!_js.jsInitialised)
+ return;
+
+ _programmaticChange++;
+
+ NSMutableDictionary *modified = [[_js.formatting getFormatting] mutableCopy];
+ if (modified == nil)
+ modified = [NSMutableDictionary dictionaryWithCapacity: 0]; // in case of JS exception
+
+ NSString *newStyle = [modified objectForKey: @"-uxwrite-paragraph-style"];
+ [modified removeObjectForKey: @"-uxwrite-paragraph-style"];
+ if ((newStyle != nil) && (newStyle.length == 0))
+ newStyle = @"p";
+
+ _lastReceivedFormatting = modified;
+
+ // FIXME: Receive paragraph style id from javascript as an (element,class) tuple, not a string
+ self.paragraphStyleId = newStyle;
+
+ self.formatting = [[EDSelectionFormatting alloc] initWithProperties: modified];
+ [self updateResizeHandles];
+
+ _programmaticChange--;
+}
+
+- (void)applyFormattingChanges
+{
+ if (!_formattingDirty)
+ return;
+ _formattingDirty = false;
+
+ if (_js.jsInitialised) {
+ // If some properties that were previously set have been removed (e.g. font-weight in the
+ // case where bold has been turned off), set these as null entries in the dictionary we
+ // pass to applyFormattingChanges(). Otherwise, they won't be considered as properties
+ // that need changing, and will retain their previous values (e.g. font-weight will
+ // remain set if there's no explicit instruction to remove it).
+ DFHashTable *collapsed = CSSCollapseProperties(_formatting.cssProperties);
+ NSMutableDictionary *changes = [NSDictionaryFromHashTable(collapsed) mutableCopy];
+ DFHashTableRelease(collapsed);
+ if (_lastReceivedFormatting != nil) {
+ for (NSString *key in _lastReceivedFormatting) {
+ if ([changes objectForKey: key] == nil)
+ [changes setObject: [NSNull null] forKey: key];
+ }
+ }
+ // FIXME: Send paragraph style id to javascript as an (element,class) tuple, not a string
+ [_js.formatting applyFormattingChangesStyle: _paragraphStyleId properties: changes];
+ }
+ [self updateFormatting];
+}
+
+- (void)detectedFormattingChange
+{
+ if (_programmaticChange == 0) {
+ _formattingDirty = YES;
+ // Invoke applyFormattingChanges after the current iteration of the event loop has finished.
+ // This way, if multiple properties of the formatting object are changed at the same time,
+ // we will only call the javascript applyFormatting() method once after all the changes
+ // have been made.
+ [self performSelector: @selector(applyFormattingChanges) withObject: nil afterDelay: 0];
+ }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
+ change:(NSDictionary *)change context:(void *)context
+{
+ if (object == _formatting)
+ [self detectedFormattingChange];
+}
+
+- (void)updateCSS
+{
+ [_delegate editorDidUpdateCSS];
+}
+
+- (void)retrieveStyles
+{
+ NSString *cssText = [_js.styles getCSSText];
+ CSSSheetUpdateFromCSSText(_styleSheet,cssText.UTF8String);
+ [_fileFormat setupInitialStyles];
+ CSSSheetUseCSSNumbering(_styleSheet);
+
+ // If any of the built-in styles (e.g. figcaption) are used in the document, but not explicitly
+ // mentioned in the CSS stylesheet text, then mark them as non-latent. This will cause their
+ // definitions to be explicitly included in the stylesheet text.
+ // FIXME: Get these sent from javascript as (element,class) pairs
+ NSDictionary *usedStyles = [_js.outline findUsedStyles];
+ if (_js.error == nil) {
+ for (NSString *rawSelector in usedStyles.allKeys) {
+ CSSStyle *style = CSSSheetLookupSelector(_styleSheet,rawSelector.UTF8String,NO,NO);
+ if ((style != nil) && (style->latent))
+ style->latent = NO;
+ }
+ }
+
+ // updateCSS causes the documentModified flag to be set, because normally it does actually
+ // represent an actual change to the document. But since this function is only called at load
+ // time, all we're actually doing here is making sure the HTML document's stylesheet is
+ // consistent with what we maintain on the Objective C side. So in this particular case we
+ // don't want it to count as a modification - otherwise simp,ly opening and closing the document
+ // would cause it to be saved, resulting in an unnecessary upload to the server
+ BOOL oldModified = _js.documentModified;
+ [self updateCSS];
+ _js.documentModified = oldModified;
+}
+
+- (void)makeStyleNonLatent:(const char *)ident
+{
+ // We must set the add parameter to YES here; otherwise, the latent parameter will be ignored
+ CSSSheetLookupSelector(_styleSheet,ident,YES,NO);
+ [self updateCSS];
+}
+
+- (void)updateOutline
+{
+ if (!_js.jsInitialised)
+ return;
+ _outlineDirty = NO;
+ _outline.json = [_js.outline getOutline];
+ [_delegate editorDidUpdateOutline];
+}
+
+- (void)setOutlineDirty
+{
+ if (!_outlineDirty) {
+ _outlineDirty = YES;
+ [self performSelector: @selector(updateOutline) withObject: nil afterDelay: 0];
+ }
+}
+
+- (void)loadAndInitSucceeded
+{
+ _jsInitOk = YES;
+ _js.jsInitialised = YES;
+ [_fileFormat finishLoad];
+ [_loadTiming addEntry: @"File format-specific initialisation"];
+ _origGenerator = [_js.main setGenerator: _generator];
+ [self retrieveStyles];
+ [self updateOutline];
+
+ NSString *localeOrEmpty = [_js.main getLanguage];
+ if (localeOrEmpty.length > 0)
+ _locale = localeOrEmpty;
+ else
+ _locale = nil;
+
+ [self updateFormatting];
+}
+
+- (void)dumpHTML
+{
+ NSString *inputStr = [_js.main getHTML];
+ NSData *input = [inputStr dataUsingEncoding: NSUTF8StringEncoding];
+ [EDHTMLTidy tidy: input isXHTML: NO editor: self completion:^(NSData *output, NSError *error) {
+ if (error != nil) {
+ debug(@"HTMLTidy failed: %@\n",FCErrorDescription(error));
+ }
+ else {
+ NSString *outputStr = [[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding];
+ debug(@"\n");
+ debug(@"------------------------------------- HTML -------------------------------------\n");
+ debug(@"%@",outputStr);
+ debug(@"--------------------------------------------------------------------------------\n");
+ debug(@"\n");
+ }
+ }];
+}
+
+- (void)saveTo:(NSString *)path completion:(EDSaveCompletion)completion
+{
+ if (!_js.jsInitialised)
+ return;
+
+ if (_activeSave == nil) {
+ _activeSave = [[EDSaveOperation alloc] initWithEditor: self path: path];
+ [_activeSave addCompletion: completion];
+ [_activeSave start];
+ }
+ else {
+ if (_pendingSave == nil)
+ _pendingSave = [[EDSaveOperation alloc] initWithEditor: self path: path];
+ [_pendingSave addCompletion: completion];
+ }
+ [self debugSaveStatus];
+}
+
+- (void)debugSaveStatus
+{
+ NSMutableString *str = [NSMutableString stringWithCapacity: 0];
+ [str appendFormat: @"Save status:"];
+
+ if (_activeSave != nil)
+ [str appendFormat: @" activeSave = %p (%d)", _activeSave, (int)_activeSave.completionCount];
+ else
+ [str appendFormat: @" activeSave = nil"];
+
+ if (_pendingSave != nil)
+ [str appendFormat: @", pendingSave = %p (%d)", _pendingSave, (int)_pendingSave.completionCount];
+ else
+ [str appendFormat: @", pendingSave = nil"];
+
+ debug(@"%@\n",str);
+}
+
+- (void)undo
+{
+ if (self.js.jsInitialised) {
+ [self.js.undoManager undo];
+ [self updateFormatting];
+ }
+}
+
+- (void)redo
+{
+ if (self.js.jsInitialised) {
+ [self.js.undoManager redo];
+ [self updateFormatting];
+ }
+}
+
+// ResizeDelegate methods
+
+- (void)resizedWidthPct:(CGFloat)widthPct
+{
+ NSString *widthStr = [NSString stringWithFormat: @"%d%%", (int)round(widthPct)];
+
+ if (_formatting.inFigure) {
+ NSString *itemId = [_js.figures getSelectedFigureId];
+ if (itemId == nil)
+ return;
+
+ NSDictionary *properties = [_js.figures getProperties: itemId];
+ if (properties == nil)
+ return;
+
+ NSString *src = [properties objectForKey: @"src"];
+ [_js.figures setProperties: itemId width: widthStr src: src];
+
+ }
+ else if (_formatting.inTable) {
+ NSString *itemId = [_js.tables getSelectedTableId];
+ if (itemId == nil)
+ return;
+
+ [_js.tables setProperties: itemId width: widthStr];
+ }
+
+ [self updateFormatting];
+}
+
+- (void)resizedColumns:(NSArray *)widthPcts
+{
+ if (_formatting.inTable) {
+ NSString *itemId = [_js.tables getSelectedTableId];
+ if (itemId == nil)
+ return;
+
+ NSMutableArray *rounded = [NSMutableArray arrayWithCapacity: widthPcts.count];
+ for (NSUInteger i = 0; i < widthPcts.count; i++) {
+ NSNumber *raw = [widthPcts objectAtIndex: i];
+ [rounded addObject: [NSNumber numberWithDouble: round(raw.doubleValue)]];
+ }
+
+ [_js.tables set: itemId colWidths: rounded];
+ }
+
+ [self updateFormatting];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDFileFormat.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDFileFormat.h b/experiments/objcFramework/EDFileFormat.h
new file mode 100644
index 0000000..d5d7417
--- /dev/null
+++ b/experiments/objcFramework/EDFileFormat.h
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import <Foundation/Foundation.h>
+#import <Editor/DocFormats.h>
+
+typedef NSComparisonResult(^Comparator)(id,id);
+
+@class EDEditor;
+@protocol EDSystemDelegate;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDFileFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface EDFileFormat : NSObject
+
+@property (weak) EDEditor *editor;
+@property (copy, readonly) NSString *fileLocalPath;
+@property (copy, readonly) NSString *absoluteHTMLPath;
+@property (assign, readonly) BOOL supportsBareStyles;
+@property (copy, readonly) NSString *imagePath;
+@property (readonly) Comparator styleComparator;
+
+- (EDFileFormat *)init;
+- (NSString *)prepareForLoad:(NSError **)error;
+- (void)finishLoad;
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion;
+- (void)setupInitialStyles;
+- (CSSStyle *)setupTableStyle;
+- (void)setupFigureStyle;
+- (void)setupOutlineStyle;
+- (NSString *)addImage:(NSData *)data extension:(NSString *)extension error:(NSError **)error;
+- (NSString *)localizedStyleName:(CSSStyle *)style;
+
++ (BOOL)create:(NSString *)filename error:(NSError **)error;
++ (EDFileFormat *)formatForExtension:(NSString *)extension;
++ (BOOL)isExtensionSupported:(NSString *)extension;
++ (NSString *)findUniqueFilenameWithPrefix:(NSString *)prefix
+ extension:(NSString *)extension
+ existingNames:(NSArray *)names;
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDFileFormat.m
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDFileFormat.m b/experiments/objcFramework/EDFileFormat.m
new file mode 100644
index 0000000..e94d1f6
--- /dev/null
+++ b/experiments/objcFramework/EDFileFormat.m
@@ -0,0 +1,968 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import "EDFileFormat.h"
+#import "EDEditor.h"
+#import "EDHTMLTidy.h"
+#import "EDJSInterface.h"
+#import "EDTiming.h"
+#import "EDStyle.h"
+#import "EDDocumentSetup.h"
+#import "EDUtil.h"
+#import <FileClient/FCError.h>
+#import <FileClient/FCUtil.h>
+
+#define L10NStyleNameHeading1 NSLocalizedString(@"StyleNameHeading1",NULL)
+#define L10NStyleNameHeading2 NSLocalizedString(@"StyleNameHeading2",NULL)
+#define L10NStyleNameHeading3 NSLocalizedString(@"StyleNameHeading3",NULL)
+#define L10NStyleNameHeading4 NSLocalizedString(@"StyleNameHeading4",NULL)
+#define L10NStyleNameHeading5 NSLocalizedString(@"StyleNameHeading5",NULL)
+#define L10NStyleNameHeading6 NSLocalizedString(@"StyleNameHeading6",NULL)
+#define L10NStyleNameParagraph NSLocalizedString(@"StyleNameParagraph",NULL)
+#define L10NStyleNameBlockQuote NSLocalizedString(@"StyleNameBlockQuote",NULL)
+#define L10NStyleNamePre NSLocalizedString(@"StyleNamePre",NULL)
+#define L10NStyleNameBody NSLocalizedString(@"StyleNameBody",NULL)
+#define L10NStyleNameFigure NSLocalizedString(@"StyleNameFigure",NULL)
+#define L10NStyleNameFigureCaption NSLocalizedString(@"StyleNameFigureCaption",NULL)
+#define L10NStyleNameTable NSLocalizedString(@"StyleNameTable",NULL)
+#define L10NStyleNameTableCaption NSLocalizedString(@"StyleNameTableCaption",NULL)
+#define L10NStyleNameTableOfContents NSLocalizedString(@"StyleNameTableOfContents",NULL)
+#define L10NStyleNameListOfFigures NSLocalizedString(@"StyleNameListOfFigures",NULL)
+#define L10NStyleNameListOfTables NSLocalizedString(@"StyleNameListOfTables",NULL)
+
+@interface HTMLFormat : EDFileFormat
+@end
+
+@interface TextFormat : EDFileFormat
+@end
+
+@interface MarkdownFormat : EDFileFormat
+@end
+
+@interface LaTeXFormat : EDFileFormat
+@end
+
+@interface WordFormat : EDFileFormat
+@end
+
+static NSString *HTML_localizedTagName(Tag tag)
+{
+ switch (tag) {
+ case HTML_H1: return L10NStyleNameHeading1;
+ case HTML_H2: return L10NStyleNameHeading2;
+ case HTML_H3: return L10NStyleNameHeading3;
+ case HTML_H4: return L10NStyleNameHeading4;
+ case HTML_H5: return L10NStyleNameHeading5;
+ case HTML_H6: return L10NStyleNameHeading6;
+ case HTML_P: return L10NStyleNameParagraph;
+ case HTML_BLOCKQUOTE: return L10NStyleNameBlockQuote;
+ case HTML_PRE: return L10NStyleNamePre;
+ case HTML_BODY: return L10NStyleNameBody;
+ case HTML_FIGURE: return L10NStyleNameFigure;
+ case HTML_FIGCAPTION: return L10NStyleNameFigureCaption;
+ case HTML_TABLE: return L10NStyleNameTable;
+ case HTML_CAPTION: return L10NStyleNameTableCaption;
+ default: return nil;
+ }
+}
+
+NSString *HTML_uiNameForSelector(const char *ident)
+{
+ NSString *rawSelector = NSStringFromC(ident);
+ if ([rawSelector isEqualToString: @"nav.tableofcontents"])
+ return L10NStyleNameTableOfContents;
+ if ([rawSelector isEqualToString: @"nav.listoffigures"])
+ return L10NStyleNameListOfFigures;
+ if ([rawSelector isEqualToString: @"nav.listoftables"])
+ return L10NStyleNameListOfTables;
+
+ NSString *elementUIName = HTML_localizedTagName(CSSSelectorGetTag(ident));
+ if (elementUIName == nil)
+ return nil;
+
+ NSString *result = NULL;
+ char *className = CSSSelectorCopyClassName(ident);
+
+ if (!CSSSelectorHasClassName(ident))
+ result = elementUIName;
+ else if (CSSSelectorGetTag(ident) == HTML_P)
+ result = NSStringFromC(className);
+ else
+ result = [NSString stringWithFormat: @"%@ (%@)", elementUIName, NSStringFromC(className)];
+
+ free(className);
+ return result;
+}
+
+@interface NSOutputStream(CompleteWrite)
+
+- (int)completeWrite:(const uint8_t *)buffer length:(NSUInteger)length;
+
+@end
+
+@implementation NSOutputStream(CompleteWrite)
+
+- (int)completeWrite:(const uint8_t *)buffer length:(NSUInteger)length
+{
+ NSUInteger offset = 0;
+ do {
+ NSUInteger remaining = length - offset;
+ NSInteger written = [self write: &buffer[offset] maxLength: remaining];
+ if (written <= 0)
+ return 0;
+ offset += written;
+ } while (offset < length);
+ return 1;
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDFileFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation EDFileFormat
+
+- (EDFileFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ return self;
+}
+
+- (NSString *)fileLocalPath
+{
+ return self.editor.delegate.editorPath;
+}
+
+- (BOOL)supportsBareStyles
+{
+ return NO;
+}
+
+- (NSString *)absoluteHTMLPath
+{
+ [NSException raise: @"FileFormat" format: @"%@ absoluteHTMLPath not implemented", self.class];
+ return nil;
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ completion([NSError error: @"FileFormat save not implemented"]);
+}
+
+- (NSString *)prepareForLoad:(NSError **)error
+{
+ [NSException raise: @"FileFormat" format: @"%@ prepareForLoad not implemented", self.class];
+ return nil;
+}
+
+- (void)finishLoad
+{
+ [NSException raise: @"FileFormat" format: @"%@ finishLoad not implemented", self.class];
+}
+
++ (BOOL)create2:(NSString *)filename error:(DFError **)error
+{
+ NSString *extension = filename.pathExtension.lowercaseString;
+ if ([extension isEqualToString: @"html"] || [extension isEqualToString: @"htm"])
+ return DFHTMLCreateDefaultFile(filename.UTF8String,error);
+ else if ([extension isEqualToString: @"docx"]) {
+ return DFWordCreateDefault(filename.UTF8String,error);
+ }
+
+ DFErrorFormat(error,"Unsupported file format: %s\n",extension.UTF8String);
+ return NO;
+}
+
++ (BOOL)create:(NSString *)filename error:(NSError **)error
+{
+ DFError *dferror = NULL;
+ BOOL ok = [self create2: filename error: &dferror];
+ if (!ok)
+ DFErrorReleaseToNSError(dferror,error);
+ return ok;
+}
+
++ (EDFileFormat *)formatForExtension:(NSString *)extension
+{
+ extension = extension.lowercaseString;
+ if ([extension isEqualToString: @"html"] || [extension isEqualToString: @"htm"])
+ return [[HTMLFormat alloc] init];
+ else if ([extension isEqualToString: @"txt"])
+ return [[TextFormat alloc] init];
+ else if ([extension isEqualToString: @"md"])
+ return [[MarkdownFormat alloc] init];
+ else if ([extension isEqualToString: @"tex"])
+ return [[LaTeXFormat alloc] init];
+ else if ([extension isEqualToString: @"docx"])
+ return [[WordFormat alloc] init];
+ return nil;
+}
+
++ (BOOL)isExtensionSupported:(NSString *)extension
+{
+ return ([self formatForExtension: extension] != nil);
+}
+
+- (void)setupInitialStyles
+{
+ // Ensure there is at least one style present for a given heading level
+ NSMutableSet *allElementNames = [NSMutableSet setWithCapacity: 0];
+ CSSSheet *styleSheet = self.editor.styleSheet;
+ const char **allSelectors = CSSSheetCopySelectors(styleSheet);
+ for (int i = 0; allSelectors[i]; i++) {
+ CSSStyle *style = CSSSheetLookupSelector(styleSheet,allSelectors[i],NO,NO);
+ [allElementNames addObject: NSStringFromC(style->elementName)];
+ }
+ free(allSelectors);
+
+ for (int i = 1; i <= 6; i++) {
+ NSString *elementName = [NSString stringWithFormat: @"h%d", i];
+ if (![allElementNames containsObject: elementName])
+ CSSSheetLookupElement(styleSheet,elementName.UTF8String,NULL,YES,YES);
+ }
+}
+
+- (CSSStyle *)setupTableStyle
+{
+ int changed = NO;
+ CSSSheet *styleSheet = self.editor.styleSheet;
+ CSSStyle *result = WordSetupTableGridStyle(styleSheet,&changed);
+ if (changed)
+ [self.editor updateCSS];
+ return result;
+}
+
+- (void)setupFigureStyle
+{
+ CSSStyle *figure = CSSSheetLookupElement(self.editor.styleSheet,"figure",NULL,NO,NO);
+ if ((figure == nil) || CSSStyleIsEmpty(figure)) {
+ figure = CSSSheetLookupElement(self.editor.styleSheet,"figure",NULL,YES,NO);
+ CSSProperties *rule = CSSStyleRule(figure);
+ CSSPut(rule,"text-align","center");
+ CSSPut(rule,"margin-left","auto");
+ CSSPut(rule,"margin-right","auto");
+ CSSPut(rule,"margin-top","12pt");
+ CSSPut(rule,"margin-bottom","12pt");
+ figure->latent = NO;
+ [self.editor updateCSS];
+ }
+}
+
+- (void)setupOutlineStyle
+{
+}
+
+- (NSString *)imagePath
+{
+ return [self.fileLocalPath stringByDeletingLastPathComponent];
+}
+
++ (NSString *)findUniqueFilenameWithPrefix:(NSString *)prefix
+ extension:(NSString *)extension
+ existingNames:(NSArray *)names
+{
+ // We first need to obtain a listing of all files in the directory, because we want a name
+ // for which there is no other file with that name and a different extension
+ // (e.g. image001.jpg and image001.png)
+
+ NSMutableSet *existingNames = [NSMutableSet setWithCapacity: 0];
+ for (NSString *filename in names)
+ [existingNames addObject: [filename.lowercaseString stringByDeletingPathExtension]];
+
+ int num = 1;
+ NSString *candidate;
+ do {
+ candidate = [NSString stringWithFormat: @"%@%03d", prefix, num];
+ num++;
+ } while ([existingNames containsObject: candidate.lowercaseString]);
+
+ return [candidate stringByAppendingPathExtension: extension];
+}
+
+- (NSString *)addImage:(NSData *)data extension:(NSString *)extension error:(NSError **)error
+{
+ NSString *baseName = [self.fileLocalPath.lastPathComponent stringByDeletingPathExtension];
+ NSString *prefix = [NSString stringWithFormat: @"%@_image", baseName];
+ return [self.editor.delegate saveResource: data prefix: prefix extension: extension error: error];
+}
+
+- (NSString *)localizedStyleName:(CSSStyle *)style
+{
+ char *cDisplayName = CSSStyleCopyDisplayName(style);
+ if (cDisplayName != NULL) {
+ NSString *displayName = NSStringFromC(cDisplayName);
+ free(cDisplayName);
+ return displayName;
+ }
+ NSString *str = HTML_uiNameForSelector(style->selector);
+ if (str != nil)
+ return str;
+ return HTML_uiNameForSelector(style->selector);
+}
+
+- (Comparator)styleComparator
+{
+ return ^NSComparisonResult(id obj1, id obj2) {
+ EDStyle *style1 = (EDStyle *)obj1;
+ EDStyle *style2 = (EDStyle *)obj2;
+ NSString *name1 = [self localizedStyleName: style1.cssStyle];
+ NSString *name2 = [self localizedStyleName: style2.cssStyle];
+ return [name1 localizedCaseInsensitiveCompare: name2];
+ };
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// HTMLFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation HTMLFormat
+
+- (HTMLFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ return self;
+}
+
+- (BOOL)supportsBareStyles
+{
+ return YES;
+}
+
+- (NSString *)absoluteHTMLPath
+{
+ return self.fileLocalPath;
+}
+
+- (NSSet *)builtinRawSelectors
+{
+ return [NSSet setWithObjects:
+ @"h1",
+ @"h2",
+ @"h3",
+ @"h4",
+ @"h5",
+ @"h6",
+ @"p",
+ @"pre",
+ @"blockquote",
+ @"@page",
+ @"body",
+ @"figure",
+ @"figcaption",
+ @"table",
+ @"caption",
+ @"nav.tableofcontents",
+ @"nav.listoffigures",
+ @"nav.listoftables",
+ nil];
+}
+
+- (NSString *)prepareForLoad:(NSError **)error
+{
+ // Nothing to do here - just return the name of the HTML file to load
+ return self.fileLocalPath;
+}
+
+- (void)finishLoad
+{
+ // Nothing to do here - default initialisation is sufficient
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ completion = [completion copy];
+ NSString *extension = self.fileLocalPath.pathExtension.lowercaseString;
+ BOOL isXHTML = [extension isEqualToString: @"xml"] || [extension isEqualToString: @"xhtml"];
+ [self.editor.saveTiming addEntry: @"JS getHTML"];
+ if (html.length == 0) {
+ completion([NSError error: @"HTML code is 0 bytes"]);
+ return;
+ }
+ html = [@"<!DOCTYPE html>\n" stringByAppendingString: html];
+
+ NSData *rawData = [html dataUsingEncoding: NSUTF8StringEncoding];
+
+ [EDHTMLTidy tidy: rawData isXHTML: isXHTML editor: self.editor completion:^(NSData *output, NSError *error) {
+ [self.editor.saveTiming addEntry: @"HTML Tidy"];
+
+ if (error != nil) {
+ completion(error);
+ return;
+ }
+
+ if ((output == nil) || (output.length == 0)) {
+ completion([NSError error: @"Output of HTML Tidy is 0 bytes"]);
+ return;
+ }
+
+ NSOutputStream *out = [NSOutputStream outputStreamToFileAtPath: filename append: NO];
+ [out open];
+ [out completeWrite: output.bytes length: output.length];
+ [out close];
+
+ if (out.streamError != nil) {
+ completion(out.streamError);
+ return;
+ }
+ [self.editor.saveTiming addEntry: @"Write temp file"];
+ completion(nil);
+ }];
+}
+
+- (BOOL)setupCellStyle:(NSString *)elementName
+{
+ CSSStyle *style = CSSSheetLookupElement(self.editor.styleSheet,elementName.UTF8String,NULL,NO,NO);
+ if (style != nil)
+ return NO;
+ style = CSSSheetLookupElement(self.editor.styleSheet,elementName.UTF8String,NULL,YES,NO);
+
+ CSSProperties *base = CSSStyleRuleForSuffix(style,"");
+ CSSProperties *firstChild = CSSStyleRuleForSuffix(style," > :first-child");
+ CSSProperties *lastChild = CSSStyleRuleForSuffix(style," > :last-child");
+
+ CSSPut(base,"border-left-width","1px");
+ CSSPut(base,"border-right-width","1px");
+ CSSPut(base,"border-top-width","1px");
+ CSSPut(base,"border-bottom-width","1px");
+
+ CSSPut(base,"border-left-style","solid");
+ CSSPut(base,"border-right-style","solid");
+ CSSPut(base,"border-top-style","solid");
+ CSSPut(base,"border-bottom-style","solid");
+
+ CSSPut(base,"border-left-color","black");
+ CSSPut(base,"border-right-color","black");
+ CSSPut(base,"border-top-color","black");
+ CSSPut(base,"border-bottom-color","black");
+
+ CSSPut(firstChild,"margin-top","0");
+ CSSPut(lastChild,"margin-bottom","0");
+ return YES;
+}
+
+- (void)fixCorruptUXWrite10CSS
+{
+ // Document was edited in UX Write >= 1.1.0 and then later edited in UX Write 1.0.x
+ // This results in corrupted counter-increment, counter-reset, and content values,
+ // due to a bug in UX Write 1.0.x which serialises these incorrectly into the stylesheet
+
+ // counter-increment and counter-reset are invalid, because WebKit's CSS serialisation
+ // code (used by 1.0.x to generate the stylesheet text for these) places commas between
+ // all of the values, which (I think) is not valid CSS
+
+ // counter is invalid, because Styles_discoverStyles() in 1.0.x stupidly removes single
+ // and double quotes from the start of any property values, on the assumption that any
+ // such values are whole strings. This turns the following:
+ //
+ // h1::before { content: counter(h1) ". " }
+ //
+ // into:
+ //
+ // h1::before { content: counter(h1) " }
+ //
+ // Because CSS strings can't end in a newline, the parser discards the rest of the
+ // property, and the ::before rule becomes empty
+
+ // To get around these problems, we simply delete all the counter-increment,
+ // counter-reset, and content properties, and re-create them. This is only going to be
+ // a problem in a very small number of cases where a document is taken from 1.1.x back
+ // to 1.0.x, e.g. due to the upgrade not being completed on all devices
+
+ CSSSheet *styleSheet = self.editor.styleSheet;
+
+ // First determine if heading numbering was on
+ // FIXME: Won't work with Word documents, in which all styles have a class name
+ CSSStyle *h1 = CSSSheetLookupElement(styleSheet,"h1",NULL,NO,NO);
+ BOOL hadHeadingNumbering = (CSSGet(CSSStyleRule(h1),"counter-increment") != NULL);
+
+ // Now clear all the counter-increment, counter-reset, and content properties
+ const char **allSelectors = CSSSheetCopySelectors(styleSheet);
+ for (int selIndex = 0; allSelectors[selIndex]; selIndex++) {
+ const char *selector = allSelectors[selIndex];
+ CSSStyle *style = CSSSheetLookupSelector(styleSheet,selector,NO,NO);
+
+ const char **allSuffixes = CSSStyleCopySuffixes(style);
+ for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
+ const char *suffix = allSuffixes[suffixIndex];
+ CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix);
+ CSSPut(properties,"counter-increment",NULL);
+ CSSPut(properties,"counter-reset",NULL);
+ CSSPut(properties,"content",NULL);
+ }
+ free(allSuffixes);
+ }
+ free(allSelectors);
+
+ // If the document *was* using heading numbering, turn it on again
+ if (hadHeadingNumbering)
+ CSSSheetSetHeadingNumbering(self.editor.styleSheet,YES);
+}
+
+- (void)transitionFromUXWrite10Numbering
+{
+ if (CSSSheetIsNumberingUsed(self.editor.styleSheet)) {
+ // Document was edited in UX Write >= 1.1.0 and then later edited in UX Write 1.0.x
+ [self fixCorruptUXWrite10CSS];
+ }
+ else {
+ // Document was created in UX Write 1.0.x and is being opened in 1.1.x for the first time
+ // Switch to using CSS counters for numbering
+ BOOL sectionNumbering = [self.editor.js.outline detectSectionNumbering];
+ if (sectionNumbering)
+ CSSSheetSetHeadingNumbering(self.editor.styleSheet,YES);
+ }
+}
+
+- (void)setupInitialStyles
+{
+ [super setupInitialStyles];
+
+ for (NSString *rawSelector in self.builtinRawSelectors)
+ CSSSheetLookupSelector(self.editor.styleSheet,rawSelector.UTF8String,YES,YES);
+
+ // Styles
+ CSSStyle *p = CSSSheetLookupElement(self.editor.styleSheet,"p",NULL,YES,NO);
+ CSSSheetSetDefaultStyle(self.editor.styleSheet,p,StyleFamilyParagraph);
+
+ NSString *origGenerator = self.editor.origGenerator;
+ if ((origGenerator != nil) && [origGenerator hasPrefix: @"UX Write 1.0"])
+ [self transitionFromUXWrite10Numbering];
+}
+
+- (CSSStyle *)setupTableStyle
+{
+// td-paragraph-margins
+// td > :first-child { margin-top: 0; }
+// td > :last-child { margin-bottom: 0; }
+// th-paragraph-margins
+// th > :first-child { margin-top: 0; }
+// th > :last-child { margin-bottom: 0; }
+// table-borders
+// table { border-collapse: collapse; margin-left: auto; margin-right: auto; }
+// td { border: 1px solid black; }
+// th { border: 1px solid black; }
+// table-caption
+// caption { caption-side: bottom; }
+
+ BOOL changed = NO;
+
+ if ([self setupCellStyle: @"td"])
+ changed = YES;
+ if ([self setupCellStyle: @"th"])
+ changed = YES;
+
+ CSSStyle *table = CSSSheetLookupElement(self.editor.styleSheet,"table",NULL,NO,NO);
+ if ((table == nil) || CSSStyleIsEmpty(table)) {
+ table = CSSSheetLookupElement(self.editor.styleSheet,"table",NULL,YES,NO);
+ CSSProperties *rule = CSSStyleRule(table);
+ CSSPut(rule,"border-collapse","collapse");
+ CSSPut(rule,"margin-left","auto");
+ CSSPut(rule,"margin-right","auto");
+ changed = YES;
+ }
+
+ if (table->latent) {
+ table->latent = NO;
+ changed = YES;
+ }
+
+ // FIXME: this is already done in useCSSNumbering - need to ignore duplication
+ CSSStyle *caption = CSSSheetLookupElement(self.editor.styleSheet,"caption",NULL,NO,NO);
+ if ((caption == nil) || CSSStyleIsEmpty(caption)) {
+ caption = CSSSheetLookupElement(self.editor.styleSheet,"caption",NULL,YES,NO);
+ CSSPut(CSSStyleRule(caption),"caption-side","bottom");
+ changed = YES;
+ }
+
+ if (changed)
+ [self.editor updateCSS];
+
+ return table;
+}
+
+- (void)setupOutlineStyle
+{
+ BOOL changed = NO;
+
+ CSSStyle *style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc1",NO,NO);
+ if (style == nil) {
+ style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc1",YES,NO);
+ CSSProperties *rule = CSSStyleRule(style);
+ CSSPut(rule,"margin-bottom","6pt");
+ CSSPut(rule,"margin-left","0pt");
+ CSSPut(rule,"margin-top","12pt");
+ changed = YES;
+ }
+
+ style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc2",NO,NO);
+ if (style == nil) {
+ style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc2",YES,NO);
+ CSSProperties *rule = CSSStyleRule(style);
+ CSSPut(rule,"margin-bottom","6pt");
+ CSSPut(rule,"margin-left","24pt");
+ CSSPut(rule,"margin-top","6pt");
+ changed = YES;
+ }
+
+ style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc3",NO,NO);
+ if (style == nil) {
+ style = CSSSheetLookupElement(self.editor.styleSheet,"p","toc3",YES,NO);
+ CSSProperties *rule = CSSStyleRule(style);
+ CSSPut(rule,"margin-bottom","6pt");
+ CSSPut(rule,"margin-left","48pt");
+ CSSPut(rule,"margin-top","6pt");
+ changed = YES;
+ }
+
+ if (changed)
+ [self.editor updateCSS];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// TextFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation TextFormat
+
+- (TextFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ return self;
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ completion([NSError error: @"TextFormat save not implemented"]);
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// MarkdownFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation MarkdownFormat
+
+- (MarkdownFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ return self;
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ completion([NSError error: @"MarkdownFormat save not implemented"]);
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// LaTeXFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation LaTeXFormat
+
+- (LaTeXFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ return self;
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ completion([NSError error: @"LaTeXFormat save not implemented"]);
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// WordFormat //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface WordFormat()
+
+@property (copy) NSString *tempDir;
+@property (copy) NSString *idPrefix;
+
+@property (copy, readonly) NSString *originalBackupPath;
+@property (copy, readonly) NSString *htmlTempDir;
+@property (copy, readonly) NSString *htmlTempFilename;
+
+@end
+
+@implementation WordFormat
+
+- (WordFormat *)init
+{
+ if (!(self = [super init]))
+ return nil;
+ _idPrefix = [[NSString alloc] initWithFormat: @"bdt%u-", arc4random()];
+ return self;
+}
+
+- (void)dealloc
+{
+ if (_tempDir != nil)
+ [[NSFileManager defaultManager] removeItemAtPath: _tempDir error: nil];
+}
+
+- (NSString *)originalBackupPath
+{
+ return [self.tempDir stringByAppendingPathComponent: @"original.docx"];
+}
+
+- (NSString *)packageTempDir
+{
+ return [self.tempDir stringByAppendingPathComponent: @"package"];
+}
+
+- (NSString *)htmlTempDir
+{
+ return [self.tempDir stringByAppendingPathComponent: @"html"];
+}
+
+- (NSString *)htmlTempFilename
+{
+ return [self.htmlTempDir stringByAppendingPathComponent: @"document.html"];
+}
+
+- (NSString *)absoluteHTMLPath
+{
+ return self.htmlTempFilename;
+}
+
+- (NSString *)prepareForLoad:(NSError **)error
+{
+ self.tempDir = FCCreateTemporaryDirectory(error);
+ if (self.tempDir == nil)
+ return nil;
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if (![fm createDirectoryAtPath: self.htmlTempDir withIntermediateDirectories: YES
+ attributes: nil error: error])
+ return nil;
+
+ NSString *resetSource = [[NSBundle mainBundle] pathForResource: @"reset" ofType: @"css"];
+ assert(resetSource != nil);
+ NSString *resetDest = [self.htmlTempDir stringByAppendingPathComponent: @"reset.css"];
+ if (![fm copyItemAtPath: resetSource toPath: resetDest error: error])
+ return nil;
+
+ if (![fm copyItemAtPath: self.fileLocalPath toPath: self.originalBackupPath error: error])
+ return nil;
+
+ DFError *dferror = NULL;
+ DFConcreteDocument *concreteDoc = DFConcreteDocumentOpenFile(self.originalBackupPath.UTF8String,&dferror);
+ if (concreteDoc == NULL) {
+ DFErrorReleaseToNSError(dferror,error);
+ return nil;
+ }
+
+ DFStorage *abstractPackage = DFStorageNewFilesystem(self.htmlTempDir.UTF8String,DFFileFormatHTML);
+ DFAbstractDocument *abstractDoc = DFAbstractDocumentNew(abstractPackage);
+
+ int ok = DFGet(concreteDoc,abstractDoc,&dferror);
+ DFDocument *htmlDoc = NULL;
+ if (ok)
+ htmlDoc = DFDocumentRetain(DFAbstractDocumentGetHTML(abstractDoc));
+
+ DFStorageRelease(abstractPackage);
+ DFAbstractDocumentRelease(abstractDoc);
+ DFConcreteDocumentRelease(concreteDoc);
+
+ if (!ok) {
+ DFErrorReleaseToNSError(dferror,error);
+ return nil;
+ }
+
+ if (htmlDoc == NULL) {
+ DFErrorFormat(&dferror,"htmlDoc is NULL");
+ DFErrorReleaseToNSError(dferror,error);
+ return nil;
+ }
+
+
+ if (!DFSerializeXMLFile(htmlDoc,0,0,self.htmlTempFilename.UTF8String,&dferror)) {
+ DFErrorReleaseToNSError(dferror,error);
+ DFDocumentRelease(htmlDoc);
+ return nil;
+ }
+
+ DFDocumentRelease(htmlDoc);
+ return self.htmlTempFilename;
+}
+
+- (void)finishLoad
+{
+}
+
+// This method is always run in a background thread, initiated via save
+- (BOOL)directSave:(NSString *)filename html:(NSString *)html error:(NSError **)error
+{
+ DFError *dferror = NULL;
+ DFDocument *htmlDoc = DFParseHTMLString(html.UTF8String,1,&dferror);
+ if (htmlDoc == nil) {
+ DFErrorReleaseToNSError(dferror,error);
+ FCErrorPrepend(error,@"Parse HTML");
+ return NO;
+ }
+
+ // Write out the HTML file in case wer're printing or generating a PDF, so it can load the
+ // page content from that
+ // FIXME: In this case, we don't really need to save the word document itself - saving *just*
+ // the HTML will be sufficient. And when we are actually saving the word document, we don't
+ // need to write out the HTML data. So this is just a temporary solution.
+ DFError *localError = NULL;
+ if (!DFSerializeXMLFile(htmlDoc,0,0,self.htmlTempFilename.UTF8String,&localError)) {
+ DFErrorReleaseToNSError(localError,error);
+ FCErrorPrepend(error,@"Save HTML");
+ DFDocumentRelease(htmlDoc);
+ return NO;
+ }
+
+ NSString *modifiedPath = [self.tempDir stringByAppendingPathComponent: @"modified.docx"];
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if (![fm copyItemAtPath: self.originalBackupPath toPath: modifiedPath error: error]) {
+ FCErrorPrepend(error,@"Copy original to modified");
+ return NO;
+ }
+
+ DFConcreteDocument *concreteDoc = DFConcreteDocumentOpenFile(modifiedPath.UTF8String,&dferror);
+ if (concreteDoc == NULL) {
+ DFErrorReleaseToNSError(dferror,error);
+ FCErrorPrepend(error,@"Open document for update");
+ return NO;
+ }
+
+ BOOL ok = NO;
+
+ DFStorage *abstractPackage = DFStorageNewFilesystem(self.htmlTempDir.UTF8String,DFFileFormatHTML);
+ DFAbstractDocument *abstractDoc = DFAbstractDocumentNew(abstractPackage);
+
+ DFAbstractDocumentSetHTML(abstractDoc,htmlDoc);
+
+ if (!DFPut(concreteDoc,abstractDoc,&dferror)) {
+ DFErrorReleaseToNSError(dferror,error);
+ FCErrorPrepend(error,@"DFPut");
+ goto end;
+ }
+
+ if ([fm fileExistsAtPath: filename] && ![fm removeItemAtPath: filename error: error]) {
+ FCErrorPrepend(error,@"Remove old version of document");
+ goto end;
+ }
+
+ if (![fm moveItemAtPath: modifiedPath toPath: filename error: error]) {
+ FCErrorPrepend(error,@"Move new version of document into place");
+ goto end;
+ }
+
+ ok = YES;
+
+end:
+ DFDocumentRelease(htmlDoc);
+ DFStorageRelease(abstractPackage);
+ DFAbstractDocumentRelease(abstractDoc);
+ DFConcreteDocumentRelease(concreteDoc);
+ return ok;
+}
+
+- (void)save:(NSString *)filename html:(NSString *)html completion:(void (^)(NSError *error))completion
+{
+ // Create a copy of the completion on the heap, in case we're passed one that's allocated on
+ // the stack
+ completion = [completion copy];
+ [self.editor.system runCommandInBackground:^BOOL(NSError **commandError) {
+ return [self directSave: filename html: html error: commandError];
+ } completion:^(NSError *completionError) {
+ completion(completionError);
+ }];
+}
+
+- (NSString *)imagePath
+{
+ return self.htmlTempDir;
+}
+
+- (void)setupInitialStyles
+{
+ [super setupInitialStyles];
+ [self.editor.js.styles setParagraphClass: @"Normal"];
+}
+
+- (NSString *)addImage:(NSData *)data extension:(NSString *)extension error:(NSError **)error
+{
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSArray *contents = [fm contentsOfDirectoryAtPath: self.imagePath error: error];
+ if (contents == nil)
+ return nil;
+
+ NSString *filename = [EDFileFormat findUniqueFilenameWithPrefix: @"image"
+ extension: extension
+ existingNames: contents];
+
+ NSString *fullPath = [self.imagePath stringByAppendingPathComponent: filename];
+ if (![data writeToFile: fullPath options: 0 error: error])
+ return nil;
+
+ return fullPath;
+}
+
+- (NSString *)localizedStyleName:(CSSStyle *)style
+{
+ char *cDisplayName = CSSStyleCopyDisplayName(style);
+ if (cDisplayName != nil) {
+ NSString *displayName = NSStringFromC(cDisplayName);
+ free(cDisplayName);
+ return [[NSBundle mainBundle] localizedStringForKey: displayName.lowercaseString
+ value: displayName
+ table: @"WordBuiltinStyles"];
+ }
+
+ return [super localizedStyleName: style];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDFindReplace.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDFindReplace.h b/experiments/objcFramework/EDFindReplace.h
new file mode 100644
index 0000000..50d4fac
--- /dev/null
+++ b/experiments/objcFramework/EDFindReplace.h
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import <Foundation/Foundation.h>
+
+#import "EDScan.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDFindOperation //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface EDFindOperation : EDScanOperation
+
+@property (copy, readonly) NSString *findTerm;
+@property (copy, readonly) NSString *replaceTerm;
+@property (assign, readonly) BOOL caseSensitive;
+@property (strong, readonly) NSRegularExpression *regex;
+
+- (EDFindOperation *)initWithEditor:(EDEditor *)editor
+ findTerm:(NSString *)findTerm
+ replaceTerm:(NSString *)replaceTerm
+ caseSensitive:(BOOL)caseSensitive
+ regex:(NSRegularExpression *)regex;
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDFindReplace.m
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDFindReplace.m b/experiments/objcFramework/EDFindReplace.m
new file mode 100644
index 0000000..59cc151
--- /dev/null
+++ b/experiments/objcFramework/EDFindReplace.m
@@ -0,0 +1,82 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import "EDFindReplace.h"
+#import "EDEditor.h"
+#import "EDScanResults.h"
+#import "EDJSInterface.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDFindOperation //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation EDFindOperation
+
+- (EDFindOperation *)initWithEditor:(EDEditor *)editor
+ findTerm:(NSString *)findTerm
+ replaceTerm:(NSString *)replaceTerm
+ caseSensitive:(BOOL)caseSensitive
+ regex:(NSRegularExpression *)regex
+{
+ if (!(self = [super initWithEditor: editor]))
+ return nil;
+ _findTerm = [findTerm copy];
+ _replaceTerm = [replaceTerm copy];
+ _caseSensitive = caseSensitive;
+ _regex = regex;
+ return self;
+}
+
+- (void)processParagraph:(EDScanParagraph *)paragraph
+{
+ NSString *text = paragraph.text;
+
+ NSMutableArray *newMatches = [NSMutableArray arrayWithCapacity: 0];
+
+ NSStringCompareOptions compareOptions = _caseSensitive ? 0 : NSCaseInsensitiveSearch;
+ NSUInteger index = 0;
+ while (true) {
+ NSRange remainder = NSMakeRange(index,text.length-index);
+ NSRange range;
+ if (_regex != nil)
+ range = [_regex rangeOfFirstMatchInString: text options: 0 range: remainder];
+ else
+ range = [text rangeOfString: _findTerm options: compareOptions range: remainder];
+ if (range.location == NSNotFound)
+ break;
+ index = range.location + range.length;
+
+ [self includeCurrentSection];
+
+ int matchId = [self.editor.js.scan addMatchStart: (int)range.location end: (int)(range.location + range.length)];
+ NSString *matchText = [text substringWithRange: range];
+ [newMatches addObject: [[EDScanMatch alloc] initWithMatchId: matchId text: matchText]];
+ }
+
+ [self foundMatches: newMatches];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/9f851a34/experiments/objcFramework/EDGeometry.h
----------------------------------------------------------------------
diff --git a/experiments/objcFramework/EDGeometry.h b/experiments/objcFramework/EDGeometry.h
new file mode 100644
index 0000000..cba3101
--- /dev/null
+++ b/experiments/objcFramework/EDGeometry.h
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file comes from the portion of the UX Write editor that
+// works on both Apple platforms (that is, it can run on either
+// OS X or iOS). It's in the repository for illustrative purposes
+// only, to assist with the creation of the framework for the
+// Corinthia editor UI. The code does not compile independently in
+// its present form.
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+typedef enum {
+ EDSizeTypeNone = 0,
+ EDSizeTypeTopLeft = 0x01,
+ EDSizeTypeTopRight = 0x02,
+ EDSizeTypeBottomLeft = 0x04,
+ EDSizeTypeBottomRight = 0x08,
+ EDSizeTypeInnerTop = 0x10,
+ EDSizeTypeInnerBottom = 0x20,
+} EDSizeType;
+
+typedef enum {
+ EDResizeEdgeTop = EDSizeTypeTopLeft | EDSizeTypeTopRight,
+ EDResizeEdgeBottom = EDSizeTypeBottomLeft | EDSizeTypeBottomRight,
+ EDResizeEdgeLeft = EDSizeTypeTopLeft | EDSizeTypeBottomLeft,
+ EDResizeEdgeRight = EDSizeTypeTopRight | EDSizeTypeBottomRight,
+ EDResizeEdgeInner = EDSizeTypeInnerTop | EDSizeTypeInnerBottom,
+} EDResizeEdge;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDItemGeometry //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface EDItemGeometry : NSObject
+
+@property (assign, readonly) CGRect contentRect; // Doc's rect for figure or table (view is slightly bigger, for resize handles)
+@property (assign, readonly) CGRect fullRect; // Includes the caption
+@property (assign, readonly) CGRect parentRect; // Rect of the figure or table's containing block (usually the body)
+@property (assign, readonly) BOOL hasCaption;
+
+// columnWidths contains NSNumber (double) objects.
+// No specific value range; calculated relative to total.
+// This property should be nil for figures, in which case no inner handles will be shown
+@property (strong, readonly) NSArray *columnWidths;
+@property (strong, readonly) NSArray *columnOffsets;
+
++ (EDItemGeometry *)fromDict:(NSDictionary *)dict;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// EDResizeDelegate //
+// //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@protocol EDResizeDelegate <NSObject>
+
+- (void)resizedWidthPct:(CGFloat)widthPct;
+- (void)resizedColumns:(NSArray *)widthPcts;
+
+@end