1 47 48 package com.lowagie.text.pdf; 49 50 import java.io.BufferedWriter ; 51 import java.io.IOException ; 52 import java.io.InputStream ; 53 import java.io.OutputStream ; 54 import java.io.OutputStreamWriter ; 55 import java.io.Reader ; 56 import java.io.Writer ; 57 import java.util.ArrayList ; 58 import java.util.HashMap ; 59 import java.util.Iterator ; 60 import java.util.List ; 61 import java.util.Map ; 62 import java.util.Stack ; 63 import java.util.StringTokenizer ; 64 65 import com.lowagie.text.xml.simpleparser.IanaEncodings; 66 import com.lowagie.text.xml.simpleparser.SimpleXMLDocHandler; 67 import com.lowagie.text.xml.simpleparser.SimpleXMLParser; 68 108 public class SimpleBookmark implements SimpleXMLDocHandler { 109 110 private ArrayList topList; 111 private Stack attr = new Stack (); 112 113 114 private SimpleBookmark() { 115 } 116 117 private static List bookmarkDepth(PdfReader reader, PdfDictionary outline, IntHashtable pages) { 118 ArrayList list = new ArrayList (); 119 while (outline != null) { 120 HashMap map = new HashMap (); 121 PdfString title = (PdfString)PdfReader.getPdfObjectRelease(outline.get(PdfName.TITLE)); 122 map.put("Title", title.toUnicodeString()); 123 PdfArray color = (PdfArray)PdfReader.getPdfObjectRelease(outline.get(PdfName.C)); 124 if (color != null && color.getArrayList().size() == 3) { 125 ByteBuffer out = new ByteBuffer(); 126 ArrayList arr = color.getArrayList(); 127 out.append(((PdfNumber)arr.get(0)).floatValue()).append(' '); 128 out.append(((PdfNumber)arr.get(1)).floatValue()).append(' '); 129 out.append(((PdfNumber)arr.get(2)).floatValue()); 130 map.put("Color", PdfEncodings.convertToString(out.toByteArray(), null)); 131 } 132 PdfNumber style = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.F)); 133 if (style != null) { 134 int f = style.intValue(); 135 String s = ""; 136 if ((f & 1) != 0) 137 s += "italic "; 138 if ((f & 2) != 0) 139 s += "bold "; 140 s = s.trim(); 141 if (s.length() != 0) 142 map.put("Style", s); 143 } 144 PdfNumber count = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.COUNT)); 145 if (count != null && count.intValue() < 0) 146 map.put("Open", "false"); 147 try { 148 PdfObject dest = PdfReader.getPdfObjectRelease(outline.get(PdfName.DEST)); 149 if (dest != null) { 150 mapGotoBookmark(map, dest, pages); } 152 else { 153 PdfDictionary action = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.A)); 154 if (action != null) { 155 if (PdfName.GOTO.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) { 156 dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D)); 157 if (dest != null) { 158 mapGotoBookmark(map, dest, pages); 159 } 160 } 161 else if (PdfName.URI.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) { 162 map.put("Action", "URI"); 163 map.put("URI", ((PdfString)PdfReader.getPdfObjectRelease(action.get(PdfName.URI))).toUnicodeString()); 164 } 165 else if (PdfName.GOTOR.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) { 166 dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D)); 167 if (dest != null) { 168 if (dest.isString()) 169 map.put("Named", dest.toString()); 170 else if (dest.isName()) 171 map.put("NamedN", PdfName.decodeName(dest.toString())); 172 else if (dest.isArray()) { 173 ArrayList arr = ((PdfArray)dest).getArrayList(); 174 StringBuffer s = new StringBuffer (); 175 s.append(arr.get(0).toString()); 176 s.append(' ').append(arr.get(1).toString()); 177 for (int k = 2; k < arr.size(); ++k) 178 s.append(' ').append(arr.get(k).toString()); 179 map.put("Page", s.toString()); 180 } 181 } 182 map.put("Action", "GoToR"); 183 PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F)); 184 if (file != null) { 185 if (file.isString()) 186 map.put("File", ((PdfString)file).toUnicodeString()); 187 else if (file.isDictionary()) { 188 file = PdfReader.getPdfObject(((PdfDictionary)file).get(PdfName.F)); 189 if (file.isString()) 190 map.put("File", ((PdfString)file).toUnicodeString()); 191 } 192 } 193 PdfObject newWindow = PdfReader.getPdfObjectRelease(action.get(PdfName.NEWWINDOW)); 194 if (newWindow != null) 195 map.put("NewWindow", newWindow.toString()); 196 } 197 else if (PdfName.LAUNCH.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) { 198 map.put("Action", "Launch"); 199 PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F)); 200 if (file == null) 201 file = PdfReader.getPdfObjectRelease(action.get(PdfName.WIN)); 202 if (file != null) { 203 if (file.isString()) 204 map.put("File", ((PdfString)file).toUnicodeString()); 205 else if (file.isDictionary()) { 206 file = PdfReader.getPdfObjectRelease(((PdfDictionary)file).get(PdfName.F)); 207 if (file.isString()) 208 map.put("File", ((PdfString)file).toUnicodeString()); 209 } 210 } 211 } 212 } 213 } 214 } 215 catch (Exception e) { 216 } 218 PdfDictionary first = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.FIRST)); 219 if (first != null) { 220 map.put("Kids", bookmarkDepth(reader, first, pages)); 221 } 222 list.add(map); 223 outline = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.NEXT)); 224 } 225 return list; 226 } 227 228 private static void mapGotoBookmark(HashMap map, PdfObject dest, IntHashtable pages) 229 { 230 if (dest.isString()) 231 map.put("Named", dest.toString()); 232 else if (dest.isName()) 233 map.put("Named", PdfName.decodeName(dest.toString())); 234 else if (dest.isArray()) 235 map.put("Page", makeBookmarkParam((PdfArray)dest, pages)); map.put("Action", "GoTo"); 237 } 238 239 private static String makeBookmarkParam(PdfArray dest, IntHashtable pages) 240 { 241 ArrayList arr = dest.getArrayList(); 242 StringBuffer s = new StringBuffer (); 243 s.append(pages.get(getNumber((PdfIndirectReference)arr.get(0)))); s.append(' ').append(arr.get(1).toString().substring(1)); 245 for (int k = 2; k < arr.size(); ++k) 246 s.append(' ').append(arr.get(k).toString()); 247 return s.toString(); 248 } 249 250 256 private static int getNumber(PdfIndirectReference indirect) 257 { 258 PdfDictionary pdfObj = (PdfDictionary)PdfReader.getPdfObjectRelease(indirect); 259 if (pdfObj.contains(PdfName.TYPE) && pdfObj.get(PdfName.TYPE).equals(PdfName.PAGES) && pdfObj.contains(PdfName.KIDS)) 260 { 261 PdfArray kids = (PdfArray)pdfObj.get(PdfName.KIDS); 262 indirect = (PdfIndirectReference)kids.arrayList.get(0); 263 } 264 return indirect.getNumber(); 265 } 266 267 274 public static List getBookmark(PdfReader reader) { 275 PdfDictionary catalog = reader.getCatalog(); 276 PdfObject obj = PdfReader.getPdfObjectRelease(catalog.get(PdfName.OUTLINES)); 277 if (obj == null || !obj.isDictionary()) 278 return null; 279 PdfDictionary outlines = (PdfDictionary)obj; 280 IntHashtable pages = new IntHashtable(); 281 int numPages = reader.getNumberOfPages(); 282 for (int k = 1; k <= numPages; ++k) { 283 pages.put(reader.getPageOrigRef(k).getNumber(), k); 284 reader.releasePage(k); 285 } 286 return bookmarkDepth(reader, (PdfDictionary)PdfReader.getPdfObjectRelease(outlines.get(PdfName.FIRST)), pages); 287 } 288 289 296 public static void eliminatePages(List list, int pageRange[]) { 297 if (list == null) 298 return; 299 for (Iterator it = list.listIterator(); it.hasNext();) { 300 HashMap map = (HashMap )it.next(); 301 boolean hit = false; 302 if ("GoTo".equals(map.get("Action"))) { 303 String page = (String )map.get("Page"); 304 if (page != null) { 305 page = page.trim(); 306 int idx = page.indexOf(' '); 307 int pageNum; 308 if (idx < 0) 309 pageNum = Integer.parseInt(page); 310 else 311 pageNum = Integer.parseInt(page.substring(0, idx)); 312 int len = pageRange.length & 0xfffffffe; 313 for (int k = 0; k < len; k += 2) { 314 if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) { 315 hit = true; 316 break; 317 } 318 } 319 } 320 } 321 List kids = (List )map.get("Kids"); 322 if (kids != null) { 323 eliminatePages(kids, pageRange); 324 if (kids.isEmpty()) { 325 map.remove("Kids"); 326 kids = null; 327 } 328 } 329 if (hit) { 330 if (kids == null) 331 it.remove(); 332 else { 333 map.remove("Action"); 334 map.remove("Page"); 335 map.remove("Named"); 336 } 337 } 338 } 339 } 340 341 351 public static void shiftPageNumbers(List list, int pageShift, int pageRange[]) { 352 if (list == null) 353 return; 354 for (Iterator it = list.listIterator(); it.hasNext();) { 355 HashMap map = (HashMap )it.next(); 356 if ("GoTo".equals(map.get("Action"))) { 357 String page = (String )map.get("Page"); 358 if (page != null) { 359 page = page.trim(); 360 int idx = page.indexOf(' '); 361 int pageNum; 362 if (idx < 0) 363 pageNum = Integer.parseInt(page); 364 else 365 pageNum = Integer.parseInt(page.substring(0, idx)); 366 boolean hit = false; 367 if (pageRange == null) 368 hit = true; 369 else { 370 int len = pageRange.length & 0xfffffffe; 371 for (int k = 0; k < len; k += 2) { 372 if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) { 373 hit = true; 374 break; 375 } 376 } 377 } 378 if (hit) { 379 if (idx < 0) 380 page = Integer.toString(pageNum + pageShift); 381 else 382 page = (pageNum + pageShift) + page.substring(idx); 383 } 384 map.put("Page", page); 385 } 386 } 387 List kids = (List )map.get("Kids"); 388 if (kids != null) 389 shiftPageNumbers(kids, pageShift, pageRange); 390 } 391 } 392 393 static void createOutlineAction(PdfDictionary outline, HashMap map, PdfWriter writer, boolean namedAsNames) { 394 try { 395 String action = (String )map.get("Action"); 396 if ("GoTo".equals(action)) { 397 String p; 398 if ((p = (String )map.get("Named")) != null) { 399 if (namedAsNames) 400 outline.put(PdfName.DEST, new PdfName(p)); 401 else 402 outline.put(PdfName.DEST, new PdfString(p, null)); 403 } 404 else if ((p = (String )map.get("Page")) != null) { 405 PdfArray ar = new PdfArray(); 406 StringTokenizer tk = new StringTokenizer (p); 407 int n = Integer.parseInt(tk.nextToken()); 408 ar.add(writer.getPageReference(n)); 409 if (!tk.hasMoreTokens()) { 410 ar.add(PdfName.XYZ); 411 ar.add(new float[]{0, 10000, 0}); 412 } 413 else { 414 String fn = tk.nextToken(); 415 if (fn.startsWith("/")) 416 fn = fn.substring(1); 417 ar.add(new PdfName(fn)); 418 for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) { 419 fn = tk.nextToken(); 420 if (fn.equals("null")) 421 ar.add(PdfNull.PDFNULL); 422 else 423 ar.add(new PdfNumber(fn)); 424 } 425 } 426 outline.put(PdfName.DEST, ar); 427 } 428 } 429 else if ("GoToR".equals(action)) { 430 String p; 431 PdfDictionary dic = new PdfDictionary(); 432 if ((p = (String )map.get("Named")) != null) 433 dic.put(PdfName.D, new PdfString(p, null)); 434 else if ((p = (String )map.get("NamedN")) != null) 435 dic.put(PdfName.D, new PdfName(p)); 436 else if ((p = (String )map.get("Page")) != null){ 437 PdfArray ar = new PdfArray(); 438 StringTokenizer tk = new StringTokenizer (p); 439 ar.add(new PdfNumber(tk.nextToken())); 440 if (!tk.hasMoreTokens()) { 441 ar.add(PdfName.XYZ); 442 ar.add(new float[]{0, 10000, 0}); 443 } 444 else { 445 String fn = tk.nextToken(); 446 if (fn.startsWith("/")) 447 fn = fn.substring(1); 448 ar.add(new PdfName(fn)); 449 for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) { 450 fn = tk.nextToken(); 451 if (fn.equals("null")) 452 ar.add(PdfNull.PDFNULL); 453 else 454 ar.add(new PdfNumber(fn)); 455 } 456 } 457 dic.put(PdfName.D, ar); 458 } 459 String file = (String )map.get("File"); 460 if (dic.size() > 0 && file != null) { 461 dic.put(PdfName.S, PdfName.GOTOR); 462 dic.put(PdfName.F, new PdfString(file)); 463 String nw = (String )map.get("NewWindow"); 464 if (nw != null) { 465 if (nw.equals("true")) 466 dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE); 467 else if (nw.equals("false")) 468 dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFFALSE); 469 } 470 outline.put(PdfName.A, dic); 471 } 472 } 473 else if ("URI".equals(action)) { 474 String uri = (String )map.get("URI"); 475 if (uri != null) { 476 PdfDictionary dic = new PdfDictionary(); 477 dic.put(PdfName.S, PdfName.URI); 478 dic.put(PdfName.URI, new PdfString(uri)); 479 outline.put(PdfName.A, dic); 480 } 481 } 482 else if ("Launch".equals(action)) { 483 String file = (String )map.get("File"); 484 if (file != null) { 485 PdfDictionary dic = new PdfDictionary(); 486 dic.put(PdfName.S, PdfName.LAUNCH); 487 dic.put(PdfName.F, new PdfString(file)); 488 outline.put(PdfName.A, dic); 489 } 490 } 491 } 492 catch (Exception e) { 493 } 495 } 496 497 public static Object [] iterateOutlines(PdfWriter writer, PdfIndirectReference parent, List kids, boolean namedAsNames) throws IOException { 498 PdfIndirectReference refs[] = new PdfIndirectReference[kids.size()]; 499 for (int k = 0; k < refs.length; ++k) 500 refs[k] = writer.getPdfIndirectReference(); 501 int ptr = 0; 502 int count = 0; 503 for (Iterator it = kids.listIterator(); it.hasNext(); ++ptr) { 504 HashMap map = (HashMap )it.next(); 505 Object lower[] = null; 506 List subKid = (List )map.get("Kids"); 507 if (subKid != null && !subKid.isEmpty()) 508 lower = iterateOutlines(writer, refs[ptr], subKid, namedAsNames); 509 PdfDictionary outline = new PdfDictionary(); 510 ++count; 511 if (lower != null) { 512 outline.put(PdfName.FIRST, (PdfIndirectReference)lower[0]); 513 outline.put(PdfName.LAST, (PdfIndirectReference)lower[1]); 514 int n = ((Integer )lower[2]).intValue(); 515 if ("false".equals(map.get("Open"))) { 516 outline.put(PdfName.COUNT, new PdfNumber(-n)); 517 } 518 else { 519 outline.put(PdfName.COUNT, new PdfNumber(n)); 520 count += n; 521 } 522 } 523 outline.put(PdfName.PARENT, parent); 524 if (ptr > 0) 525 outline.put(PdfName.PREV, refs[ptr - 1]); 526 if (ptr < refs.length - 1) 527 outline.put(PdfName.NEXT, refs[ptr + 1]); 528 outline.put(PdfName.TITLE, new PdfString((String )map.get("Title"), PdfObject.TEXT_UNICODE)); 529 String color = (String )map.get("Color"); 530 if (color != null) { 531 try { 532 PdfArray arr = new PdfArray(); 533 StringTokenizer tk = new StringTokenizer (color); 534 for (int k = 0; k < 3; ++k) { 535 float f = Float.parseFloat(tk.nextToken()); 536 if (f < 0) f = 0; 537 if (f > 1) f = 1; 538 arr.add(new PdfNumber(f)); 539 } 540 outline.put(PdfName.C, arr); 541 } catch(Exception e){} } 543 String style = (String )map.get("Style"); 544 if (style != null) { 545 style = style.toLowerCase(); 546 int bits = 0; 547 if (style.indexOf("italic") >= 0) 548 bits |= 1; 549 if (style.indexOf("bold") >= 0) 550 bits |= 2; 551 if (bits != 0) 552 outline.put(PdfName.F, new PdfNumber(bits)); 553 } 554 createOutlineAction(outline, map, writer, namedAsNames); 555 writer.addToBody(outline, refs[ptr]); 556 } 557 return new Object []{refs[0], refs[refs.length - 1], new Integer (count)}; 558 } 559 560 570 public static void exportToXMLNode(List list, Writer out, int indent, boolean onlyASCII) throws IOException { 571 String dep = ""; 572 for (int k = 0; k < indent; ++k) 573 dep += " "; 574 for (Iterator it = list.iterator(); it.hasNext();) { 575 HashMap map = (HashMap )it.next(); 576 String title = null; 577 out.write(dep); 578 out.write("<Title "); 579 List kids = null; 580 for (Iterator e = map.entrySet().iterator(); e.hasNext();) { 581 Map.Entry entry = (Map.Entry ) e.next(); 582 String key = (String ) entry.getKey(); 583 if (key.equals("Title")) { 584 title = (String ) entry.getValue(); 585 continue; 586 } 587 else if (key.equals("Kids")) { 588 kids = (List ) entry.getValue(); 589 continue; 590 } 591 else { 592 out.write(key); 593 out.write("=\""); 594 String value = (String ) entry.getValue(); 595 if (key.equals("Named") || key.equals("NamedN")) 596 value = SimpleNamedDestination.escapeBinaryString(value); 597 out.write(SimpleXMLParser.escapeXML(value, onlyASCII)); 598 out.write("\" "); 599 } 600 } 601 out.write(">"); 602 if (title == null) 603 title = ""; 604 out.write(SimpleXMLParser.escapeXML(title, onlyASCII)); 605 if (kids != null) { 606 out.write("\n"); 607 exportToXMLNode(kids, out, indent + 1, onlyASCII); 608 out.write(dep); 609 } 610 out.write("</Title>\n"); 611 } 612 } 613 614 641 public static void exportToXML(List list, OutputStream out, String encoding, boolean onlyASCII) throws IOException { 642 String jenc = IanaEncodings.getJavaEncoding(encoding); 643 Writer wrt = new BufferedWriter (new OutputStreamWriter (out, jenc)); 644 exportToXML(list, wrt, encoding, onlyASCII); 645 } 646 647 656 public static void exportToXML(List list, Writer wrt, String encoding, boolean onlyASCII) throws IOException { 657 wrt.write("<?xml version=\"1.0\" encoding=\""); 658 wrt.write(SimpleXMLParser.escapeXML(encoding, onlyASCII)); 659 wrt.write("\"?>\n<Bookmark>\n"); 660 exportToXMLNode(list, wrt, 1, onlyASCII); 661 wrt.write("</Bookmark>\n"); 662 wrt.flush(); 663 } 664 665 671 public static List importFromXML(InputStream in) throws IOException { 672 SimpleBookmark book = new SimpleBookmark(); 673 SimpleXMLParser.parse(book, in); 674 return book.topList; 675 } 676 677 683 public static List importFromXML(Reader in) throws IOException { 684 SimpleBookmark book = new SimpleBookmark(); 685 SimpleXMLParser.parse(book, in); 686 return book.topList; 687 } 688 689 public void endDocument() { 690 } 691 692 public void endElement(String tag) { 693 if (tag.equals("Bookmark")) { 694 if (attr.isEmpty()) 695 return; 696 else 697 throw new RuntimeException ("Bookmark end tag out of place."); 698 } 699 if (!tag.equals("Title")) 700 throw new RuntimeException ("Invalid end tag - " + tag); 701 HashMap attributes = (HashMap )attr.pop(); 702 String title = (String )attributes.get("Title"); 703 attributes.put("Title", title.trim()); 704 String named = (String )attributes.get("Named"); 705 if (named != null) 706 attributes.put("Named", SimpleNamedDestination.unEscapeBinaryString(named)); 707 named = (String )attributes.get("NamedN"); 708 if (named != null) 709 attributes.put("NamedN", SimpleNamedDestination.unEscapeBinaryString(named)); 710 if (attr.isEmpty()) 711 topList.add(attributes); 712 else { 713 HashMap parent = (HashMap )attr.peek(); 714 List kids = (List )parent.get("Kids"); 715 if (kids == null) { 716 kids = new ArrayList (); 717 parent.put("Kids", kids); 718 } 719 kids.add(attributes); 720 } 721 } 722 723 public void startDocument() { 724 } 725 726 public void startElement(String tag, HashMap h) { 727 if (topList == null) { 728 if (tag.equals("Bookmark")) { 729 topList = new ArrayList (); 730 return; 731 } 732 else 733 throw new RuntimeException ("Root element is not Bookmark: " + tag); 734 } 735 if (!tag.equals("Title")) 736 throw new RuntimeException ("Tag " + tag + " not allowed."); 737 HashMap attributes = new HashMap (h); 738 attributes.put("Title", ""); 739 attributes.remove("Kids"); 740 attr.push(attributes); 741 } 742 743 public void text(String str) { 744 if (attr.isEmpty()) 745 return; 746 HashMap attributes = (HashMap )attr.peek(); 747 String title = (String )attributes.get("Title"); 748 title += str; 749 attributes.put("Title", title); 750 } 751 } 752 | Popular Tags |