You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@corinthia.apache.org by Peter Kelly <pm...@apache.org> on 2015/04/24 17:59:13 UTC

Re: incubator-corinthia git commit: Copy parts of src/word/lenses to src/odf/lenses/.

I’d like to recommend a different strategy: Rather than copying across code from the Word filter, I think it would be easier to organically “grow” the code based on implementing support for the ODF types. I noticed that some of the code copied across is data structures and algorithms that are very specific to OOXML and don’t have any corresponding meaning in ODF - fields for example work completely differently.

The same goes for ODFCoverterGet; the set of steps you’d use pre- and post- main translation would be very different to those of word. Things like collapsing bookmarks, fixing list representation etc. similarly have no analogous implementation for ODF.

Here’s what I would recommend:

- Start by doing just an ODF to HTML translation, without regard to going the other way.
- Do a traversal of the DOM tree of the content.xml file from the ODF package, producing a HTML document as you go. You could do this in either one recursive function, or several different functions (one for each type, e.g. paragraph, table, list etc).
- This will give you a handle on the basic conversion process in action, and you can produce a number of test cases as you go. When you’ve got these I can help you get them into a similar format as the automated tests for Word; but we’ll cross that bridge when we come to it.
- After this, we can look at adding node identifiers to the generated ODF elements, and then implementing the put operatino

—
Dr Peter M. Kelly
pmkelly@apache.org

PGP key: http://www.kellypmk.net/pgp-key <http://www.kellypmk.net/pgp-key>
(fingerprint 5435 6718 59F0 DD1F BFA0 5E46 2523 BAA1 44AE 2966)

> On 24 Apr 2015, at 8:31 pm, gbg@apache.org wrote:
> 
> Repository: incubator-corinthia
> Updated Branches:
>  refs/heads/master ff322cb4f -> 83720d9dd
> 
> 
> Copy parts of src/word/lenses to src/odf/lenses/.
> 
> * DocFormats/filters/odf/src/text/ODFText.c
> 
>  (ODFTextGet): Uncomment section.  Function now 'works'.
> 
> * DocFormats/filters/ooxml/CMakeLists.txt
> 
>  (set): Add new group: GroupOOXMLODFLenses.
>         Add files: src/odf/lenses/ODFField.*
> 
>  (add_library): Add groups GroupOOXMODF and GroupOOXMLODFLenses.
> 
> * DocFormats/filters/ooxml/src/odf/ODFConverter.c
> 
>  (#include): Add temporary stdio.h for easy debugging.
> 
>  (WordConverterGetConcrete): Change Error message.  Add temporary
>    magic number in lieu of OFFICE_DOCUMENT not seeming to be the
>    right choice.  Move commmenting out below ODF_simplifyFields()
>    call.
> 
> * DocFormats/filters/ooxml/src/odf/lenses/ODFField.h
> 
>  (): New file.  String replaced copy of  /word/lenses/
> 
> * DocFormats/filters/ooxml/src/odf/lenses/ODFField.c
> 
>  (): New file.  String replaced copy of  /word/lenses/
> 
> 
> Project: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/repo
> Commit: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/commit/83720d9d
> Tree: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/tree/83720d9d
> Diff: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/diff/83720d9d
> 
> Branch: refs/heads/master
> Commit: 83720d9ddad4d1021e3cf0b416ebd7a761771cae
> Parents: ff322cb
> Author: Gabriela Gibson <gb...@apache.org>
> Authored: Fri Apr 24 14:31:43 2015 +0100
> Committer: Gabriela Gibson <gb...@apache.org>
> Committed: Fri Apr 24 14:31:43 2015 +0100
> 
> ----------------------------------------------------------------------
> DocFormats/filters/odf/src/text/ODFText.c       |   4 +-
> DocFormats/filters/ooxml/CMakeLists.txt         |  10 +-
> DocFormats/filters/ooxml/src/odf/ODFConverter.c |  13 +-
> .../filters/ooxml/src/odf/lenses/ODFField.c     | 494 +++++++++++++++++++
> .../filters/ooxml/src/odf/lenses/ODFField.h     |  27 +
> 5 files changed, 540 insertions(+), 8 deletions(-)
> ----------------------------------------------------------------------
> 
> 
> http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/83720d9d/DocFormats/filters/odf/src/text/ODFText.c
> ----------------------------------------------------------------------
> diff --git a/DocFormats/filters/odf/src/text/ODFText.c b/DocFormats/filters/odf/src/text/ODFText.c
> index 683e2d6..417aea9 100644
> --- a/DocFormats/filters/odf/src/text/ODFText.c
> +++ b/DocFormats/filters/odf/src/text/ODFText.c
> @@ -41,7 +41,7 @@ DFDocument *ODFTextGet(DFStorage *concreteStorage, DFStorage *abstractStorage, c
>     ok = 1;
> 
> end:
> -    /*
> +
>     ODFPackageRelease(odfPackage);
>     if (ok) {
>         return htmlDoc;
> @@ -50,7 +50,7 @@ end:
>         DFDocumentRelease(htmlDoc);
>         return NULL;
>     }
> -    */
> +
>     return NULL;
> }
> 
> 
> http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/83720d9d/DocFormats/filters/ooxml/CMakeLists.txt
> ----------------------------------------------------------------------
> diff --git a/DocFormats/filters/ooxml/CMakeLists.txt b/DocFormats/filters/ooxml/CMakeLists.txt
> index 4696309..3ba0b01 100644
> --- a/DocFormats/filters/ooxml/CMakeLists.txt
> +++ b/DocFormats/filters/ooxml/CMakeLists.txt
> @@ -29,6 +29,13 @@ set(GroupOOXMLODF
>     src/odf/ODFConverter.c
>     src/odf/ODFConverter.h)
> 
> +###
> +## group ooxml odf lenses objects
> +###
> +set(GroupOOXMLODFLenses
> +    src/odf/lenses/ODFField.c
> +    src/odf/lenses/ODFField.h)
> +    
> 
> ###
> ## group ooxml word objects
> @@ -160,7 +167,8 @@ add_library(ooxml OBJECT
>     ${GroupOOXMLWordFormatting}
>     ${GroupOOXMLWordLenses}
>     ${GroupOOXMLWordTests}
> -    ${GroupOOXMLODF})
> +    ${GroupOOXMLODF}
> +    ${GroupOOXMLODFLenses})
> 
> source_group(src\\common           FILES ${GroupOOXMLCommon})
> source_group(src\\word             FILES ${GroupOOXMLWord})
> 
> http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/83720d9d/DocFormats/filters/ooxml/src/odf/ODFConverter.c
> ----------------------------------------------------------------------
> diff --git a/DocFormats/filters/ooxml/src/odf/ODFConverter.c b/DocFormats/filters/ooxml/src/odf/ODFConverter.c
> index c7d79e8..3a21567 100644
> --- a/DocFormats/filters/ooxml/src/odf/ODFConverter.c
> +++ b/DocFormats/filters/ooxml/src/odf/ODFConverter.c
> @@ -54,6 +54,8 @@
> #include <stdlib.h>
> #include <string.h>
> 
> +// 1i: debugging include --- remove
> +#include <stdio.h>
> /*
> static int isWhitespaceRun(DFNode *run)
> {
> @@ -688,20 +690,21 @@ DFNode *WordConverterGetConcrete(WordPutData *put, DFNode *abstract)
> 
> int ODFConverterGet(DFDocument *html, DFStorage *abstractStorage, ODFPackage *package, const char *idPrefix, DFError **error)
> {
> -    // 1i: contentDoc is a crude guess here.
>     if (package->contentDoc == NULL) {
> -        DFErrorFormat(error,"document.xml not found");
> +        DFErrorFormat(error,"content.xml not found");
>         return 0;
>     }
> 
> -    // 1i: asssuming that OFFIC means AOO and so the WORD_DOCUMENT equivalent is OFFICE_DOCUMENT
> -    DFNode *odfDocument = DFChildWithTag(package->contentDoc->docNode,OFFICE_DOCUMENT);
> +    // 1i: this line needs work on the xml tags.
> +    printf("OFFICE_DOCUMENT is %d\n", OFFICE_DOCUMENT);
> +    DFNode *odfDocument = DFChildWithTag(package->contentDoc->docNode,1469 /* magic number for what I found in gdb */);
>     if (odfDocument == NULL) {
>         DFErrorFormat(error,"odf:document not found");
>         return 0;
>     }
> +
> +    int haveFields = ODF_simplifyFields(package);
>     /*
> -    int haveFields = Word_simplifyFields(package);
>     Word_mergeRuns(package);
> 
>     WordConverter *converter = WordConverterNew(html,abstractStorage,package,idPrefix);
> 
> http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/83720d9d/DocFormats/filters/ooxml/src/odf/lenses/ODFField.c
> ----------------------------------------------------------------------
> diff --git a/DocFormats/filters/ooxml/src/odf/lenses/ODFField.c b/DocFormats/filters/ooxml/src/odf/lenses/ODFField.c
> new file mode 100644
> index 0000000..e7abf45
> --- /dev/null
> +++ b/DocFormats/filters/ooxml/src/odf/lenses/ODFField.c
> @@ -0,0 +1,494 @@
> +// 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.
> +
> +#include "DFPlatform.h"
> +#include "ODFField.h"
> +/*
> +#include "ODFLenses.h"
> +#include "ODFBookmark.h"
> +#include "ODFObjects.h"
> +#include "ODFPackage.h"
> +#include "ODFCaption.h"
> +*/
> +
> +#include "DFDOM.h"
> +#include "DFXML.h"
> +#include "DFString.h"
> +#include "DFArray.h"
> +#include "DFCommon.h"
> +#include <assert.h>
> +#include <ctype.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +/*
> +static void ODFFieldPut(ODFPutData *put, DFNode *abstract, DFNode *concrete);
> +
> +const char **ODF_parseField(const char *str)
> +{
> +    size_t len = strlen(str);
> +    DFArray *components = DFArrayNew((DFCopyFunction)xstrdup,free);
> +
> +    size_t start = 0;
> +    int inString = 0;
> +    for (size_t pos = 0; pos <= len; pos++) {
> +        if (inString) {
> +            if ((pos == len) || (str[pos] == '"')) {
> +                char *comp = DFSubstring(str,start,pos);
> +                DFArrayAppend(components,(char *)comp);
> +                free(comp);
> +                start = pos+1;
> +                inString = 0;
> +            }
> +        }
> +        else {
> +            if ((pos == len) || isspace(str[pos])) {
> +                if (pos > start) {
> +                    char *comp = DFSubstring(str,start,pos);
> +                    DFArrayAppend(components,(char *)comp);
> +                    free(comp);
> +                }
> +                start = pos+1;
> +            }
> +            else if (str[pos] == '"') {
> +                inString = 1;
> +                start = pos+1;
> +            }
> +        }
> +    }
> +
> +    const char **result = DFStringArrayFlatten(components);
> +    DFArrayRelease(components);
> +    return result;
> +}
> +
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +//                                                                                                //
> +//                                       DOM helper methods                                       //
> +//                                                                                                //
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +
> +typedef struct {
> +    DFNode *commonAncestor;
> +    DFNode *beginAncestor;
> +    DFNode *endAncestor;
> +} CommonAncestorInfo;
> +
> +static CommonAncestorInfo findCommonAncestor(DFNode *beginNode, DFNode *endNode)
> +{
> +    CommonAncestorInfo info = { NULL, NULL, NULL };
> +    for (DFNode *beginA = beginNode; beginA != NULL; beginA = beginA->parent) {
> +        for (DFNode *endA = endNode; endA != NULL; endA = endA->parent) {
> +            if (beginA->parent == endA->parent) {
> +                info.commonAncestor = beginA->parent;
> +                info.beginAncestor = beginA;
> +                info.endAncestor = endA;
> +                return info;
> +            }
> +        }
> +    }
> +    return info;
> +}
> +
> +static void removeNodes(DFNode *beginNode, DFNode *endNode)
> +{
> +    CommonAncestorInfo common = findCommonAncestor(beginNode,endNode);
> +    assert(common.commonAncestor != NULL);
> +    assert(common.beginAncestor != NULL);
> +    assert(common.endAncestor != NULL);
> +
> +    DFNode *begin = beginNode;
> +    while (begin != common.beginAncestor) {
> +        DFNode *parent = begin->parent;
> +        if (begin->next != NULL)
> +            DFRemoveNode(begin->next);
> +        else
> +            begin = parent;
> +    }
> +
> +    DFNode *end = endNode;
> +    while (end != common.endAncestor) {
> +        DFNode *parent = end->parent;
> +        if (end->prev != NULL)
> +            DFRemoveNode(end->prev);
> +        else
> +            end = parent;
> +    }
> +
> +    if (common.beginAncestor != common.endAncestor) {
> +        while (common.beginAncestor->next != common.endAncestor)
> +            DFRemoveNode(common.beginAncestor->next);
> +    }
> +
> +    while ((beginNode != NULL) && (beginNode->first == NULL) && (beginNode->tag != WORD_DOCUMENT)) {
> +        DFNode *parent = beginNode->parent;
> +        DFRemoveNode(beginNode);
> +        beginNode = parent;
> +    }
> +
> +    while ((endNode != NULL) && (endNode->first == NULL) && (endNode->tag != WORD_DOCUMENT)) {
> +        DFNode *parent = endNode->parent;
> +        DFRemoveNode(endNode);
> +        endNode = parent;
> +    }
> +}
> +*/
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +//                                                                                                //
> +//                                       ODFSimplification                                       //
> +//                                                                                                //
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +
> +typedef struct ODFSimplification {
> +    DFBuffer *instrText;
> +    DFNode *beginNode;
> +    DFNode *endNode;
> +    int haveFields;
> +    int inSeparate;
> +    int depth;
> +} ODFSimplification;
> +
> +static void replaceField(ODFSimplification *simp)
> +{
> +    assert(simp->instrText != NULL);
> +    assert(simp->beginNode != NULL);
> +    assert(simp->endNode != NULL);
> +
> +    if ((simp->beginNode->parent->tag == WORD_R) && (simp->endNode->parent->tag == WORD_R)) {
> +        DFNode *beginRun = simp->beginNode->parent;
> +
> +        DFNode *simple = DFCreateElement(simp->beginNode->doc,WORD_FLDSIMPLE);
> +        DFSetAttribute(simple,WORD_INSTR,simp->instrText->data);
> +        DFInsertBefore(beginRun->parent,simple,beginRun);
> +        // 1i: plug in later
> +        //removeNodes(simp->beginNode,simp->endNode);
> +    }
> +
> +    DFBufferRelease(simp->instrText);
> +    simp->instrText = NULL;
> +    simp->beginNode = NULL;
> +    simp->endNode = NULL;
> +
> +    simp->haveFields = 1;
> +}
> +
> +static void simplifyRecursive(ODFSimplification *simp, DFNode *node)
> +{
> +    switch (node->tag) {
> +        case WORD_FLDCHAR: {
> +            const char *type = DFGetAttribute(node,WORD_FLDCHARTYPE);
> +            if (DFStringEquals(type,"begin")) {
> +                if (simp->depth == 0) {
> +                    DFBufferRelease(simp->instrText);
> +                    simp->instrText = DFBufferNew();
> +                    simp->beginNode = node;
> +                    simp->endNode = NULL;
> +                    simp->inSeparate = 0;
> +                }
> +                simp->depth++;
> +            }
> +            else if (DFStringEquals(type,"end") && (simp->depth > 0)) {
> +                simp->depth--;
> +                if (simp->depth == 0) {
> +                    simp->endNode = node;
> +                    replaceField(simp);
> +                }
> +            }
> +            else if (DFStringEquals(type,"separate")) {
> +                if (simp->depth == 1)
> +                    simp->inSeparate = 1;
> +            }
> +            break;
> +        }
> +        case WORD_INSTRTEXT: {
> +            if ((simp->depth == 1) && !simp->inSeparate) {
> +                char *value = DFNodeTextToString(node);
> +                DFBufferFormat(simp->instrText,"%s",value);
> +                free(value);
> +            }
> +            break;
> +        }
> +    }
> +
> +    DFNode *next;
> +    for (DFNode *child = node->first; child != NULL; child = next) {
> +        next = child->next;
> +        simplifyRecursive(simp,child);
> +    }
> +}
> +
> +int ODF_simplifyFields(ODFPackage *package)
> +{
> +    ODFSimplification simp;
> +    bzero(&simp,sizeof(ODFSimplification));
> +    simplifyRecursive(&simp,package->contentDoc->docNode);
> +    DFBufferRelease(simp.instrText);
> +    return simp.haveFields;
> +}
> +
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +//                                                                                                //
> +//                                          ODFFieldLens                                         //
> +//                                                                                                //
> +////////////////////////////////////////////////////////////////////////////////////////////////////
> +/*
> +typedef enum {
> +    ODFRefUnknown,
> +    ODFRefNum,
> +    ODFRefText,
> +    ODFRefDirection,
> +    ODFRefCaptionText,
> +    ODFRefLabelNum,
> +} ODFRefType;
> +
> +static const char *ODFRefTypeClassName(ODFRefType refType)
> +{
> +    switch (refType) {
> +        case ODFRefText:
> +            return DFRefTextClass;
> +        case ODFRefDirection:
> +            return DFRefDirectionClass;
> +        case ODFRefCaptionText:
> +            return DFRefCaptionTextClass;
> +        case ODFRefLabelNum:
> +            return DFRefLabelNumClass;
> +        case ODFRefNum:
> +        default:
> +            return DFRefNumClass;
> +    }
> +}
> +
> +static ODFRefType ODFRefTypeGet(const char **args, ODFBookmark *bookmark)
> +{
> +    size_t argCount = DFStringArrayCount(args);
> +    ODFRefType type = ODFRefText;
> +
> +    for (size_t argno = 2; argno < argCount; argno++) {
> +        const char *arg = args[argno];
> +        if (!strcmp(arg,"\\r"))
> +            type = ODFRefNum; // Numbered reference (normal)
> +        else if (!strcmp(arg,"\\n"))
> +            type = ODFRefNum; // Numbered reference (no context)
> +        else if (!strcmp(arg,"\\w"))
> +            type = ODFRefNum; // Numbered reference (full context)
> +        else if (!strcmp(arg,"\\p"))
> +            type = ODFRefDirection;
> +    }
> +
> +    if ((bookmark->type == ODFBookmarkTable) ||
> +        (bookmark->type == ODFBookmarkFigure) ||
> +        (bookmark->type == ODFBookmarkEquation)) {
> +        if (type == ODFRefText) {
> +            DFNode *p = ODFFindContainingParagraph(bookmark->element);
> +            if (p != NULL) {
> +                CaptionParts parts = ODFBookmarkGetCaptionParts(bookmark);
> +
> +                if (parts.beforeNum && !parts.num && !parts.afterNum)
> +                    type = ODFRefCaptionText;
> +                else if (parts.beforeNum && parts.num && !parts.afterNum)
> +                    type = ODFRefLabelNum;
> +            }
> +        }
> +    }
> +
> +    return type;
> +}
> +
> +static DFNode *ODFFieldGet(ODFGetData *get, DFNode *concrete)
> +{
> +    if (concrete->tag != WORD_FLDSIMPLE)
> +        return NULL;;
> +
> +    const char *instr = DFGetAttribute(concrete,WORD_INSTR);
> +    if (instr != NULL) {
> +        const char **args = ODF_parseField(instr);
> +        size_t argCount = DFStringArrayCount(args);
> +
> +        if ((argCount >= 2) && !strcmp(args[0],"REF")) {
> +            ODFBookmark *bookmark = ODFObjectsBookmarkWithName(get->conv->objects,args[1]);
> +            if ((bookmark != NULL) && (bookmark->target != NULL)) {
> +
> +                ODFRefType type = ODFRefTypeGet(args,bookmark);
> +
> +                DFNode *a = ODFConverterCreateAbstract(get,HTML_A,concrete);
> +                DFFormatAttribute(a,HTML_HREF,"#%s%u",get->conv->idPrefix,bookmark->target->seqNo);
> +                DFSetAttribute(a,HTML_CLASS,ODFRefTypeClassName(type));
> +
> +                free(args);
> +                return a;
> +            }
> +        }
> +        else if ((argCount >= 1) && !strcmp(args[0],"TOC")) {
> +
> +            if ((argCount >= 2) && !strcmp(args[1],"\\o")) {
> +                DFNode *nav = ODFConverterCreateAbstract(get,HTML_NAV,concrete);
> +                DFSetAttribute(nav,HTML_CLASS,DFTableOfContentsClass);
> +                free(args);
> +                return nav;
> +            }
> +            else if ((argCount >= 3) && !strcmp(args[1],"\\c")) {
> +                // FIXME: The names "Figure" and "Table" here will be different if the document
> +                // was created in a language other than English. We need to look through the
> +                // document to figure out which counter names are used in captions adjacent to
> +                // figures and tables to know what the counter names used in the document
> +                // actually are.
> +
> +                // Another option might be just to collect a static list of names used in all the
> +                // major languages and base the detection on that. These would need to be checked
> +                // with multiple versions of word, as the names used could in theory change
> +                // between releases.
> +
> +                // We should keep track of a set of "document parameters", which record the names
> +                // used for figure and table counters, as well as the prefixes used on numbered
> +                // figures and tables. The latter would correspond to the content property of the
> +                // caption::before and figcaption::before CSS rules.
> +
> +                if (!strcmp(args[2],"Figure")) {
> +                    DFNode *nav = ODFConverterCreateAbstract(get,HTML_NAV,concrete);
> +                    DFSetAttribute(nav,HTML_CLASS,DFListOfFiguresClass);
> +                    free(args);
> +                    return nav;
> +                }
> +                else if (!strcmp(args[2],"Table")) {
> +                    DFNode *nav = ODFConverterCreateAbstract(get,HTML_NAV,concrete);
> +                    DFSetAttribute(nav,HTML_CLASS,DFListOfTablesClass);
> +                    free(args);
> +                    return nav;
> +                }
> +            }
> +        }
> +
> +        DFNode *span = ODFConverterCreateAbstract(get,HTML_SPAN,concrete);
> +        DFSetAttribute(span,HTML_CLASS,DFFieldClass);
> +        DFNode *text = DFCreateTextNode(get->conv->html,instr);
> +        DFAppendChild(span,text);
> +        free(args);
> +        return span;
> +    }
> +    return NULL;
> +}
> +
> +static int ODFFieldIsVisible(ODFPutData *put, DFNode *concrete)
> +{
> +    return 1;
> +}
> +
> +static DFNode *ODFFieldCreate(ODFPutData *put, DFNode *abstract)
> +{
> +    DFNode *concrete = DFCreateElement(put->contentDoc,WORD_FLDSIMPLE);
> +    // fldSimple elements are required to have an instr attribute (even if it's empty), so set
> +    // it here in case update doesn't change it for some reason
> +    DFSetAttribute(concrete,WORD_INSTR,"");
> +    ODFFieldPut(put,abstract,concrete);
> +    put->conv->haveFields = 1;
> +    return concrete;
> +}
> +
> +static const char *bookmarkNameForHtmlId(ODFConverter *converter, const char *htmlId, const char *refClass)
> +{
> +    DFNode *htmlElem = DFElementForIdAttr(converter->html,htmlId);
> +    if (htmlElem == NULL)
> +        return NULL;
> +    switch (htmlElem->tag) {
> +        case HTML_H1:
> +        case HTML_H2:
> +        case HTML_H3:
> +        case HTML_H4:
> +        case HTML_H5:
> +        case HTML_H6: {
> +            DFNode *labelSpan = htmlElem->first;
> +            if ((labelSpan == NULL) || (labelSpan->tag != HTML_SPAN))
> +                return NULL;;
> +            const char *labelClass = DFGetAttribute(labelSpan,HTML_CLASS);
> +            if (!DFStringEquals(labelClass,DFBookmarkClass))
> +                return NULL;
> +            return DFGetAttribute(labelSpan,WORD_NAME);
> +        }
> +        case HTML_FIGURE:
> +        case HTML_TABLE: {
> +            ODFCaption *caption = ODFObjectsCaptionForTarget(converter->objects,htmlElem);
> +            if (caption == NULL)
> +                return NULL;
> +            if (DFStringEquals(refClass,DFRefTextClass) && (caption->textBookmark != NULL))
> +                return caption->textBookmark->bookmarkName;
> +            else if (DFStringEquals(refClass,DFRefLabelNumClass) && (caption->labelNumBookmark != NULL))
> +                return caption->labelNumBookmark->bookmarkName;
> +            else if (DFStringEquals(refClass,DFRefCaptionTextClass) && (caption->captionTextBookmark != NULL))
> +                return caption->captionTextBookmark->bookmarkName;
> +            else if (caption->textBookmark != NULL)
> +                return caption->textBookmark->bookmarkName; // default is entire caption
> +        }
> +        default:
> +            return NULL;
> +    }
> +}
> +
> +static void ODFFieldPut(ODFPutData *put, DFNode *abstract, DFNode *concrete)
> +{
> +    switch (abstract->tag) {
> +        case HTML_SPAN: {
> +            const char *className = DFGetAttribute(abstract,HTML_CLASS);
> +            if (!DFStringEquals(className,DFFieldClass))
> +                return;
> +            char *text = DFNodeTextToString(abstract);
> +            DFSetAttribute(concrete,WORD_INSTR,text);
> +            free(text);
> +            break;
> +        }
> +        case HTML_A: {
> +            const char *href = DFGetAttribute(abstract,HTML_HREF);
> +            if ((href == NULL) || (href[0] != '#'))
> +                return;;
> +
> +            const char *targetId = &href[1];
> +            const char *className = DFGetAttribute(abstract,HTML_CLASS);
> +            if (className == NULL)
> +                className = "";;
> +            const char *bookmarkName = bookmarkNameForHtmlId(put->conv,targetId,className);
> +            if (bookmarkName == NULL)
> +                return;;
> +
> +            DFNode *htmlElem = DFElementForIdAttr(put->conv->html,targetId);
> +            if ((htmlElem != NULL) && ((htmlElem->tag == HTML_TABLE) || (htmlElem->tag == HTML_FIGURE))) {
> +                if (!DFStringEquals(className,DFRefTextClass) &&
> +                    !DFStringEquals(className,DFRefLabelNumClass) &&
> +                    !DFStringEquals(className,DFRefCaptionTextClass))
> +                    className = DFRefTextClass;
> +            }
> +
> +            if (DFStringEquals(className,DFRefTextClass) ||
> +                DFStringEquals(className,DFRefLabelNumClass) ||
> +                DFStringEquals(className,DFRefCaptionTextClass))
> +                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\h ",bookmarkName);
> +            else if (DFStringEquals(className,DFRefDirectionClass))
> +                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\p \\h ",bookmarkName);
> +            else
> +                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\r \\h ",bookmarkName);
> +            break;
> +        }
> +    }
> +}
> +
> +ODFLens ODFFieldLens = {
> +    .isVisible = ODFFieldIsVisible,
> +    .get = ODFFieldGet,
> +    .put = ODFFieldPut,
> +    .create = ODFFieldCreate,
> +    .remove = NULL, // LENS FIXME
> +};
> +
> +*/
> 
> http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/83720d9d/DocFormats/filters/ooxml/src/odf/lenses/ODFField.h
> ----------------------------------------------------------------------
> diff --git a/DocFormats/filters/ooxml/src/odf/lenses/ODFField.h b/DocFormats/filters/ooxml/src/odf/lenses/ODFField.h
> new file mode 100644
> index 0000000..99e4252
> --- /dev/null
> +++ b/DocFormats/filters/ooxml/src/odf/lenses/ODFField.h
> @@ -0,0 +1,27 @@
> +// 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.
> +
> +#ifndef DocFormats_ODFField_h
> +#define DocFormats_ODFField_h
> +
> +#include "ODFPackage.h"
> +
> +const char **ODF_parseField(const char *cstr);
> +
> +int ODF_simplifyFields(ODFPackage *package);
> +
> +#endif
>