ColdFusion CFDOCUMENT со ссылками на другие PDF-файлы

В данный момент я создаю PDF-файл с использованием тега cfdocument. PDF-файл представляет собой не что иное, как набор ссылок на другие PDF-файлы.

Итак, я создаю этот индекс PDF, и все ссылки являются HREF.

<a href="Another_PDF.pdf">Another PDF</a>

если я установлю для атрибута localURL значение «нет», в моих URL-адресах будет весь веб-путь:

<a href="http://www.mywebsite.com/media/PDF/Another_PDF.pdf">Another PDF</a>

если я устанавливаю для атрибута localURL значение «да», я получаю:

<a href="File:/D:/website/media/PDF/Another_PDF.pdf">Another PDF</a>

Таким образом, этот индексный PDF-файл будет помещен на компакт-диск, а все связанные PDF-файлы будут находиться рядом с ним, поэтому мне нужна относительная ссылка... больше похоже на:

<a href="Another_PDF.pdf">Another PDF</a>

cfdocument, похоже, этого не делает. Я могу изменить имя файла документа и сделать его "File:///Another_PDF.pdf", но это не работает либо потому, что я не знаю букву диска компакт-диска ... или если файлы собираются оказаться внутри каталога на компакт-диске.

Есть ли способ (возможно, с помощью iText или чего-то еще) открыть PDF-файл после его создания и преобразовать URL-ссылки в фактические теги PDF GoTo?

Я знаю, что это своего рода натяжка, но я не в своем уме с этим.

Итак, мне удалось попасть в Объекты, но я все еще борюсь.

Преобразование из:

5 0 obj<</C[0 0 1]/Border[0 0 0]/A<</URI(File:///75110_002.PDF)/S/URI>>/Subtype/Link/Rect[145 502 184 513]>>endobj

К этому:

19 0 obj<</SGoToR/D[0/XYZ null null 0]/F(75110_002.PDF)>>endobj 
20 0 obj<</Subtype/Link/Rect[145 502 184 513]/Border[0 0 0]/A 19 0 R>>endobj 

Вау, это действительно надирает мне задницу! :)

Итак, мне удалось открыть документ, пройтись по аннотациям ссылок, зафиксировать координаты Rect и ссылку на имя документа (сохраненное в массив структур), а затем успешно удалить аннотацию, которая была ссылкой URI.

Итак, теперь я подумал, что теперь я могу перебрать этот массив структур и поместить аннотации обратно в документ, используя метод createLink или метод setAction. Но все примеры этих методов, которые я видел, привязаны к фрагменту (текста). Но в моем документе уже есть текст, поэтому мне не нужно переделывать текстовые ссылки, мне просто нужно вернуть ссылки на то же место.

Поэтому я решил, что могу снова открыть документ и найти фактический текст, который был ссылкой, а затем прикрепить setAction к уже существующему фрагменту текста... Я не могу найти текст!

я сосать! :)


person Joe Simes    schedule 06.01.2014    source источник
comment
Структура каталогов компакт-диска известна/всегда одинакова?   -  person gfrobenius    schedule 06.01.2014
comment
Это не... или в идеале он должен быть динамичным. Прямо сейчас я смотрю на компакт-диск, на котором все PDF-файлы (включая индекс) находятся в каталоге с именем STD_FLD, поэтому путь на моем компьютере — J:\STD_FLD, потому что мой дисковод для компакт-дисков — это мой J-диск.   -  person Joe Simes    schedule 06.01.2014
comment
Индекс PDF действительно должен быть относительно самого себя. Вы должны иметь возможность перетаскивать все PDF-файлы с компакт-диска и хранить их в любом месте, а ссылки должны работать.   -  person Joe Simes    schedule 06.01.2014
comment
Я не думаю, что это возможно. Прочтите это: stackoverflow.com/a/15193744/3112803   -  person gfrobenius    schedule 07.01.2014
comment
Да, я знаю, как работают схемы URI. Меня больше интересует преобразование ссылок URI в PDF со ссылками GoToR. Надеюсь, с помощью iText.   -  person Joe Simes    schedule 07.01.2014
comment
В этом потоке есть пример обновления действий ссылки путем изменения аннотаций PDF. Это для iTextSharp 5.x, но, если не считать имен пакетов, java-код не сильно отличается.   -  person Leigh    schedule 07.01.2014
comment
Я так близок!! Я могу добраться до рассматриваемых Onjects, но я до сих пор не нахожу способа конвертировать один тип ссылки в другой:   -  person Joe Simes    schedule 08.01.2014
comment
Вы должны получить ключ URI из вашего объекта PDFAction. Он возвращает строку. Затем замените строку на ключ URI. Это похоже на замену значения в структуре. т.е. theActionObject.put( pdfName.URI, "theNewValueHere");   -  person Leigh    schedule 08.01.2014
comment
Я заказал книгу Бруно Лоуаги, надеюсь, сегодня она будет в офисе!! Скрестив пальцы, это поможет мне изменить ссылки URI на ссылки GoToR, и я могу пометить @Leigh как ответ ниже!   -  person Joe Simes    schedule 09.01.2014


Ответы (2)


В этом потоке есть пример обновления действий ссылки путем изменения аннотаций PDF. Он написан на iTextSharp 5.x, но java-код мало чем отличается.

Тема дает четкое объяснение того, как работают аннотации. Но, чтобы подвести итог, вам нужно прочитать исходный PDF-файл и просмотреть отдельные страницы для аннотаций. Извлеките ссылки и используйте что-то вроде getFileFromPath(), чтобы заменить их только именем файла.

Мне было любопытно, поэтому я сделал быстрое и уродливое преобразование кода iTextSharp выше. Отказ от ответственности, он не очень проверен:

/**
    Usage:

    util = createObject("component", "path.to.ThisComponent");
    util.fixLinks( "c:/path/to/sourceFile.pdf", "c:/path/to/newFile.pdf");

*/
component {

    /**
        Convert all absolute links, in the given pdf, to relative links (file name only)
        @source - absolute path to the source pdf file
        @destination - absolute path to save copy
    */
    public function fixLinks( string source, string destination) {
        // initialize objects
        Local.reader = createObject("java", "com.lowagie.text.pdf.PdfReader").init( arguments.source );
        Local.pdfName = createObject("java", "com.lowagie.text.pdf.PdfName");

        // check each page for hyperlinks
        for ( Local.i = 1; Local.i <= Local.reader.getNumberOfPages(); Local.i++) {

            //Get all of the annotations for the current page
            Local.page = Local.reader.getPageN( Local.i );
            Local.annotations = Local.page.getAsArray( Local.PdfName.ANNOTS ).getArrayList();

            // search annotations for links
            for (Local.x = 1; !isNull( Local.annotations) && Local.x < arrayLen(Local.annotations); Local.x++) {

                  // get current properties
                  Local.current     = Local.annotations[ Local.x ]; 
                  Local.dictionary  = Local.reader.getPdfObject( Local.current );
                  Local.subType     = Local.dictionary.get( Local.PdfName.SUBTYPE );
                  Local.action      = Local.dictionary.get( Local.PdfName.A );
                  Local.hasLink     = true;

                  //Skip this item if it does not have a link AND action
                  if (Local.subType != Local.PdfName.LINK || isNull(Local.action)) {
                       Local.hasLink = false;
                  }
                  //Skip this item if it does not have a URI
                  if ( Local.hasLink && Local.action.get( Local.PdfName.S ) != Local.PdfName.URI ) {
                       Local.hasLink = false;
                  } 

                  //If it is a valid URI, update link
                  if (Local.hasLink) {
                      // extract file name from URL
                      Local.oldLink = Local.action.get( Local.pdfName.URI );
                      Local.newLink  = getFileFromPath( Local.oldLink );

                      // replace link
                      // WriteDump("Changed link from ["& Local.oldLink &"] ==> ["& Local.newLink &"]");
                      Local.pdfString = createObject("java", "com.lowagie.text.pdf.PdfString");
                      Local.action.put( Local.pdfName.URI, Local.pdfString.init( Local.newLink ) );
                  }
            }

        }

        // save all pages to new file   
        copyPDF( Local.reader , arguments.destination );    
    }

    /**
        Copy all pages in pdfReader to the given destination file
        @pdfReader - pdf to copy
        @destination - absolute path to save copy
    */
    public function copyPDF( any pdfReader, string destination) {
        try {

          Local.doc = createObject("java", "com.lowagie.text.Document").init();
          Local.out = createObject("java", "java.io.FileOutputStream").init( arguments.destination );
          Local.writer = createObject("java", "com.lowagie.text.pdf.PdfCopy").init(Local.doc, Local.out);

          // open document and save individual pages        
          Local.doc.open();
          for (Local.i = 1; i <= arguments.pdfReader.getNumberOfPages(); Local.i++) {
              Local.writer.addPage( Local.writer.getImportedPage( arguments.pdfReader,  Local.i) );
          }
          Local.doc.close();
        }
        finally 
        {
            // cleanup
            if (structKeyExists(Local, "doc")) { Local.doc.close(); }
            if (structKeyExists(Local, "writer")) { Local.writer.close(); }
            if (structKeyExists(Local, "out")) { Local.out.close(); }
        }
    }

}
person Leigh    schedule 07.01.2014
comment
Ли Большое спасибо. Я собираюсь попробовать ваш код прямо сейчас. Это даст мне возможность редактировать путь в URI. Я не уверен, отредактирую ли я его с File:///75110_002.PDF просто на 75110_002.PDF, если гиперссылка будет работать. Моя конечная цель - изменить ключ S словаря действий аннотаций с URI на GoToR. Я не уверен, что это вообще возможно. Потому что ключи для URI и аннотации GoToR совершенно разные. - person Joe Simes; 08.01.2014
comment
Просто чтобы вы знали, что код работает как чемпион! Итак, теперь TOC PDF, находящийся в каталоге на моем рабочем столе или записанный на компакт-диск, будет открывать все ссылки на PDF-файлы в веб-браузере, потому что это все еще URI! :) - person Joe Simes; 08.01.2014
comment
Рад, что это помогло. Код можно было бы немного оптимизировать, но я подумал, что причудливый и грязный пример будет полезно иметь в архивах :) - person Leigh; 08.01.2014
comment
Конечно, я вошел и закомментировал это Local.action.put( Local.pdfName.URI, Local.pdfString.init( Local.newLink ) ); и добавил Local.action.put( Local.pdfName.S, Local.pdfString.init( '/GoToR' ) ); и Local.action.put( Local.pdfName.D, Local.pdfString.init( '[0, /XYZ, null, null, 0]' ) ); и Local.action.put( Local.pdfName.F, Local.pdfString.init( Local.newLink ) ); и Local.action.remove( Local.pdfName.URI ); и это полностью сломано!! :) - person Joe Simes; 08.01.2014
comment
Я не могу проверить это сейчас. Однако, глядя на источник предполагает, что существует несколько конструкторов PDFAction, настроенных для GoToR. Попробуйте создать новый PdfAction, а затем заменить существующий в словаре. - person Leigh; 09.01.2014
comment
Это то, что я собираюсь попробовать сегодня, и хитрость для меня заключается в том, чтобы правильно выполнить материал RECT ... большинство кодов, которые я вижу для этого, создают фрагмент, а затем прикрепляют к нему PDFAction ... но я уже получил кусок! С нетерпением жду книгу Бруно!! Я бы лучше потратил 60 долларов на пиво! - person Joe Simes; 09.01.2014

Я наконец понял:

public function resetLinks( string source, string destination) {

    try {

        // initialize objects
        Local.reader = createObject("java", "com.lowagie.text.pdf.PdfReader").init( arguments.source );
        Local.pdfName = createObject("java", "com.lowagie.text.pdf.PdfName");
        Local.annot = createObject("java", "com.lowagie.text.pdf.PdfAnnotation");
        Local.out = createObject("java", "java.io.FileOutputStream").init( arguments.destination );
        Local.stamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init(Local.reader, Local.out);
        Local.PdfAction = createObject("java", "com.lowagie.text.pdf.PdfAction");
        Local.PdfRect = createObject("java", "com.lowagie.text.Rectangle");
        Local.PdfBorderArray = createObject("java", "com.lowagie.text.pdf.PdfBorderArray").init(javacast("float", "0"), javacast("float", "0"), javacast("float", "0"));
        Local.newAnnots = [];

        // check each page for hyperlinks
        // Save the data to a structure then write it to an array 
        // then delete the hyperlink Annotation
        for ( Local.i = 1; Local.i <= Local.reader.getNumberOfPages(); Local.i = Local.i + 1) {
            //Get all of the annotations for the current page
            Local.page = Local.reader.getPageN( Local.i );
            Local.annotations = Local.page.getAsArray( Local.PdfName.ANNOTS ).getArrayList();

            // search annotations for links
            for (Local.x = arrayLen(Local.annotations); !isNull( Local.annotations) && Local.x > 0; Local.x--) {
                // get current properties
                Local.current     = Local.annotations[ Local.x ]; 
                Local.dictionary  = Local.reader.getPdfObject( Local.current );
                Local.subType     = Local.dictionary.get( Local.PdfName.SUBTYPE );
                Local.action      = Local.dictionary.get( Local.PdfName.A );
                Local.hasLink     = true;

                //Skip this item if it does not have a link AND action
                if (Local.subType != Local.PdfName.LINK || isNull(Local.action)) {
                    Local.hasLink = false;
                }
                //Skip this item if it does not have a URI
                if ( Local.hasLink && Local.action.get( Local.PdfName.S ) != Local.PdfName.URI ) {
                    Local.hasLink = false;
                } 

                //If it is a valid URI, update link
                if (Local.hasLink) {
                    // extract file name from URL
                    Local.oldLink = Local.action.get( Local.pdfName.URI );
                    Local.newLink  = getFileFromPath( Local.oldLink );
                    Local.Rect = Local.dictionary.Get(PdfName.Rect);
                    arrayStruct = StructNew();
                    arrayStruct.rectSTR = Local.Rect.toString();
                    arrayStruct.link = Local.newLink;
                    arrayStruct.page = Local.i;
                    ArrayAppend(Local.newAnnots, arrayStruct);
                    // Delete
                    Local.annotations.remove(Local.current);
                }
            }

        }

        // Now really remove them!   
        Local.reader.RemoveUnusedObjects();

        // Now loop over the saved annotations and put them back!!
        for ( Local.z = 1; Local.z <= ArrayLen(Local.newAnnots); Local.z++) {
            // Parse the rect we got save into an Array
            theRectArray = ListToArray(ReplaceNoCase(ReplaceNoCase(Local.newAnnots[z].rectSTR, "[", ""), "]", ""));
            // Create the GoToR action
            theAction = Local.PdfAction.gotoRemotePage(javacast("string", '#Local.newAnnots[z].link#'), javacast("string", '#Local.newAnnots[z].link#'), javacast("boolean", "false"), javacast("boolean", "false"));
            // Create the Link Annotation with the above Action and the Rect
            theAnnot = Local.annot.createLink(Local.stamper.getWriter(), Local.PdfRect.init(javacast("int", theRectArray[1]), javacast("int", theRectArray[2]), javacast("int", theRectArray[3]), javacast("int", theRectArray[4])), Local.annot.HIGHLIGHT_INVERT, theAction);
            // Remove the border the underlying underlined text will flag item as a link
            theAnnot.setBorder(Local.PdfBorderArray);
            // Add the Annotation to the Page
            Local.stamper.addAnnotation(theAnnot, Local.newAnnots[z].page);
        }
    }

    finally {
        // cleanup
        if (structKeyExists(Local, "reader")) { Local.reader.close(); }
        if (structKeyExists(Local, "stamper")) { Local.stamper.close(); }
        if (structKeyExists(Local, "out")) { Local.out.close(); }
    }
}

Я не смог бы сделать это без помощи Ли!!

person Joe Simes    schedule 15.01.2014