How can I delete a part?

Developer
Jul 28, 2011 at 3:24 AM

I am trying to delete a part.  It does not work.  See my sample code below.

bool DeletePart(const char* zipFilePath, const char* partPath) {
    bool ret = false;
    opcInitLibrary();
    opcContainer* c = opcContainerOpen(_X(zipFilePath), OPC_OPEN_READ_WRITE, NULL, NULL);
    if (c != NULL) {
        opcPart part = opcPartFind(c, _X(partPath), NULL, 0);
        if (part != OPC_PART_INVALID) {
            opc_error_t result = opcPartDelete(c, _X(partPath));
            if (result == OPC_ERROR_NONE) {
                ret = true;
                opcContainerClose(c, OPC_CLOSE_TRIM);
            }
        }
    }
    opcFreeLibrary();
    return ret;
}

What's wrong with my code? Or it is a bug in libopc?

By the way, I have been using this library for several days, it is really easy to use.  Thank you for the great job.

Coordinator
Jul 28, 2011 at 10:32 AM

Hi!

Turned out to be an error in libopc. I fixed it in change set 9532.

Can you give it a try?

Thanks a lot for the feedback!

Florian

Developer
Jul 29, 2011 at 2:07 AM

Hi Florian,

I have just tested the latest version 9532, it is fixed.

Rene

Coordinator
Jul 29, 2011 at 8:36 AM

Hi Rene,

I was just wondering whether the delete methods should also remove all dependent parts? E.g. if you delete the "word/document.xml" part from a .DOCX file should the header, footer and picture parts be deleted too?

Any opinion?

Florian

Developer
Aug 18, 2011 at 9:59 AM

Hi Florian,

I am so sorry for the delayed response.

In my opinion, I am glad to have such a delete method to remove all dependent parts, but this is a little bit dangerous, I may not realize what other part will be deleted.  Sometimes I still need a way to delete only the specified part.  I would like there is an option in delete method to allow user to choose if the dependent parts will be deleted.

Rene

Coordinator
Aug 19, 2011 at 8:01 AM

Hi Rene,

 

thanks for the feedback. I'll like the idea of a parameterized delete. Another idea I had in mind was to "gargabe collect" parts thee OPC_CLOSE_TRIM is used.

 

Florian

Developer
Aug 22, 2011 at 9:36 AM

Hi Florian,

Sounds good.  I am looking forward to the enhancement.

I have written some unit test for my own libopc c++ wrapper.  Two cases failed to pass the UT.   Let me show the errors in C code.

1. Cannot remove the package file after calling opcContainerClose.  I found the static method opcFileClose was never called.

    opcContainer* c = opcContainerOpen(_X("c:\\OOXMLI1.docx"), OPC_OPEN_READ_WRITE, NULL, NULL);
    opcContainerClose(c, OPC_CLOSE_NOW);

    int ret = remove("c:\\OOXMLI1.docx");  // always fail

 

2. Deleting a part which was just inserted cause a crash.

    opcContainer* c = NULL;
    c = opcContainerOpen(_X("c:\\OOXMLI1.docx"), OPC_OPEN_READ_WRITE, NULL, NULL);

    bool added = false;
    bool removed = false;
    if (c != NULL) {
        opcPart part = opcPartFind(c, _X("/word/media/new_add.jpg"), NULL, 0);
        if (part == OPC_PART_INVALID) {
            part = opcPartCreate(c, _X("/word/media/new_add.jpg"), NULL, 0);
            opcContainerOutputStream* stream = opcContainerCreateOutputStream(c, part, OPC_COMPRESSIONOPTION_NORMAL);
            if (stream != NULL) {
                FILE *source = fopen("c:\\new_add.jpg", "rb");
                if (source != NULL) {
                    opc_uint32_t result = 0;
                    opc_uint8_t buf[100] = {0};
                    while(!feof(source)) {
                        result = fread(buf, sizeof(char), sizeof(buf), source);
                        if (result != 0) {
                            opcContainerWriteOutputStream(stream, buf, result);
                        }
                    }
                    fclose(source);
                    added = true;
                }
                opcContainerCloseOutputStream(stream);
            }

            if (added) {
                // Remove the part
                part = opcPartFind(c, _X("/word/media/new_add.jpg"), NULL, 0);
                if (part != OPC_PART_INVALID) {
                    opc_error_t ret_del = opcPartDelete(c, _X("/word/media/new_add.jpg"));
                    if (ret_del == OPC_ERROR_NONE) {
                        removed = true;
                    }
                }
            }
        }
    }   

    opcContainerClose(c, OPC_CLOSE_NOW);

Would you please look into these problems.  Thanks.

 

Rene

Coordinator
Aug 22, 2011 at 10:26 AM

Thanks a lot for the testing!

 

I fixed 1. upstream and I'm having a look into 2.

 

Keep on testing ;-)

 

Florian

Developer
Sep 6, 2011 at 7:29 AM

Hi Florian,

How about the problem 2?

I am trying to create a part which has an override content type.  I found a sync problem. See code below.

    opcContainer* c = opcContainerOpen(_X("c:\\OOXMLI1.docx"), OPC_OPEN_READ_WRITE, NULL, NULL);

    int count = 0;
    for(const xmlChar *type=opcContentTypeFirst(c);
        NULL!=type;
        type=opcContentTypeNext(c, type)) {
        count++;
    }

    // count = 12, Correct

    // create a part having an override content type.
    opcPart part = opcPartCreate(c, _X("thumbnail.jpg"), _X("my_test_content_type"), 0); // override content type

    opcContainerOutputStream* stream = opcContainerCreateOutputStream(c, part, OPC_COMPRESSIONOPTION_NORMAL);
    if (stream != NULL) {
        FILE *source = fopen("c:\\test.jpg", "rb");
        if (source != NULL) {
            opc_uint32_t result = 0;
            opc_uint8_t buf[100] = {0};
            while(!feof(source)) {
                result = fread(buf, sizeof(char), sizeof(buf), source);
                if (result != 0) {
                    opcContainerWriteOutputStream(stream, buf, result);
                }
            }
            fclose(source);
        }
        opcContainerCloseOutputStream(stream);
    }

    count = 0;
    for(const xmlChar *type=opcContentTypeFirst(c);
        NULL!=type;
        type=opcContentTypeNext(c, type)) {
        count++;
    }   

    // count = 12, I expected the count should be 13.

   If I close the container, and open it again the count is 13.  It seems that there is no way to register a new override content type in memory.  I guess you need to call insertType method when calling opcPartCreate.  Am I right? 

 

Rene

Coordinator
Sep 6, 2011 at 4:19 PM

Hi Rene,

thanks for reporting the issue!

I'm currently a bit busy with another project. I'll free some time to look into the issue tomorrow.

Florian

Developer
Sep 8, 2011 at 7:09 AM

Hi Florian,

When updating an existing part, is it possible update the OPC file without totally rewriting it? Conceptually, it would seem possible to mark parts of the file as empty and append the new sections, but I doubt that the underlying libraries support that type of operation.

Rene

Coordinator
Sep 8, 2011 at 12:04 PM

Hi Rene,

the problem here is the "streaming mode" of ZIP resp. OPC. This mode is important if you e.g. receive an XPS document from a slow connection and you want to display the first pages as you download the rest... So simply appending new content without dealing with the "garbage" will not work. 

However when you use OPC_CLOSE_NOW libopc tries to write as little to the disk as possible in order to create a valid OPC document. When you use OPC_CLOSE_TRIM the gargabe will be collected and a "minimal" (in size) container will be created.

Another technique are "interleaved parts" of OPC. In theory OPC can split a stream into different pieces. I have a lot of functionality for piece support already in the libopc however its not quite ready... On the TODO list...

Hope that helps,

Florian

P.S: will start looking into the issue you reported now..

Coordinator
Sep 8, 2011 at 12:57 PM

Hi Rene,

 

> I guess you need to call insertType method when calling opcPartCreate.  Am I right? 

Yep. Forgot to register the type: http://libopc.codeplex.com/SourceControl/changeset/changes/11113#opc%2fpart.c

 

Thanks again for finding the issue,

 

Florian

Coordinator
Sep 8, 2011 at 2:51 PM

>How about the problem 2?

Fixed upstream. Can you give it a try?

Developer
Sep 9, 2011 at 6:03 AM

Hi Florian,

It makes sense for me to understand how libopc write a part.   Thank you.

The two problems have been resolved.

Rene