1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.pdf.models.jni; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.graphics.Rect; 22 import android.graphics.RectF; 23 import android.graphics.pdf.content.PdfPageLinkContent; 24 import android.graphics.pdf.flags.Flags; 25 import android.graphics.pdf.utils.Preconditions; 26 import android.net.Uri; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 import java.util.stream.Collectors; 32 33 /** 34 * Represents the bounds of links as a {@code List<List<Rect>>}, where 35 * the first {@code List<Rect>} is all of the rectangles needed to bound the 36 * first link, and so on. Most links will be surrounded with a single Rect. 37 * <p> 38 * Internally, data is stored as 1-dimensional Lists, to avoid the overhead of 39 * a large amount of single-element lists. 40 * <p> 41 * Also contains the URL index of each link - so {@link #get} returns the 42 * rectangles that bound the link, and {@link #getUrls} returns the URLs that is 43 * linked to. 44 * 45 * @hide 46 */ 47 // TODO(b/324536951): Remove this class after updating the native code to directly use 48 // PdfPageLinkContent 49 public class LinkRects extends ListOfList<Rect> { 50 /** Required at the JNI layer to detect no links and return an empty {@link LinkRects} */ 51 public static final LinkRects NO_LINKS = new LinkRects(Collections.<Rect>emptyList(), 52 Collections.<Integer>emptyList(), Collections.<String>emptyList()); 53 private final List<Rect> mRects; 54 private final List<Integer> mLinkToRect; 55 private final List<String> mUrls; 56 LinkRects(@onNull List<Rect> rects, @NonNull List<Integer> linkToRect, @NonNull List<String> urls)57 public LinkRects(@NonNull List<Rect> rects, @NonNull List<Integer> linkToRect, 58 @NonNull List<String> urls) { 59 super(rects, linkToRect); 60 this.mRects = Preconditions.checkNotNull(rects, "rects cannot be null"); 61 this.mLinkToRect = Preconditions.checkNotNull(linkToRect, "linkToRect cannot be null"); 62 this.mUrls = Preconditions.checkNotNull(urls, "urls cannot be null"); 63 } 64 65 /** Returns the list of bounds for the embedded weblinks. */ getRects()66 public List<Rect> getRects() { 67 return mRects; 68 } 69 70 /** 71 * Returns the mapping list of bounds to the consecutive link. 72 * 73 * @see #unflattenToList() 74 */ getLinkToRect()75 public List<Integer> getLinkToRect() { 76 return mLinkToRect; 77 } 78 79 /** Returns the list of embedded links on the page of the document. */ getUrls()80 public List<String> getUrls() { 81 return mUrls; 82 } 83 84 /** 85 * Un-flattens the list and converts to the public class. 86 * <p>As an example, in case there are 2 weblinks on the page of the document with the 1st link 87 * overflowing to the next line, the {@link LinkRects} would have the following values - 88 * <pre> 89 * LinkRects( 90 * mRects=[Rect(l1, t1, r1, b1), Rect(l2, t2, r2, b2), Rect(l3, t3, r3, b3)], 91 * mLinkToRect=[0,2,3], 92 * mUrls=[url1, url2] 93 * ) 94 * 95 * // In this case, the first link is represented by the first two {@link Rect}. The mapping to 96 * // these Rect is done through the {@code mLinkToRect} array. This is the flattened 97 * // representation of the links and bounds. Using the method below, we can un-flatten this 98 * // to the following representation - 99 * List( 100 * PdfPageLinkContent( 101 * bounds = [Rect(l1, t1, r1, b1), Rect(l2, t2, r2, b2)], 102 * url = url1 103 * ), 104 * PdfPageLinkContent( 105 * bounds = [Rect(l3, t3, r3, b3)], 106 * url = url2 107 * ), 108 * ) 109 * </pre> 110 */ 111 @FlaggedApi(Flags.FLAG_ENABLE_PDF_VIEWER) unflattenToList()112 public List<PdfPageLinkContent> unflattenToList() { 113 List<PdfPageLinkContent> boundedLinks = new ArrayList<>(); 114 for (int index = 0; index < mLinkToRect.size(); index++) { 115 List<Rect> bounds = new ArrayList<>(); 116 int boundsForCurrentLink = (index + 1 < mLinkToRect.size()) ? mLinkToRect.get(index + 1) 117 : mRects.size(); 118 for (int boundIndex = mLinkToRect.get(index); boundIndex < boundsForCurrentLink; 119 boundIndex++) { 120 bounds.add(mRects.get(boundIndex)); 121 } 122 boundedLinks.add(new PdfPageLinkContent( 123 bounds.stream().map(RectF::new).collect(Collectors.toList()), 124 Uri.parse(mUrls.get(index)))); 125 } 126 127 return boundedLinks; 128 } 129 } 130