Skip to content

Commit cb29a4a

Browse files
committed
[ADD] TTFDataStream.createSubView() to create a subview without copying arrays
[ADD] RandomAccessReadUncachedDataStream that doesn't read input stream to byte[]
1 parent 2685fe9 commit cb29a4a

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

fontbox/src/main/java/org/apache/fontbox/ttf/RandomAccessReadDataStream.java

+19
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
import java.io.ByteArrayInputStream;
2020
import java.io.IOException;
2121
import java.io.InputStream;
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
2224

2325
import org.apache.pdfbox.io.IOUtils;
2426
import org.apache.pdfbox.io.RandomAccessRead;
27+
import org.apache.pdfbox.io.RandomAccessReadBuffer;
2528

2629
/**
2730
* An implementation of the TTFDataStream using RandomAccessRead as source.
@@ -30,6 +33,8 @@
3033
*/
3134
class RandomAccessReadDataStream extends TTFDataStream
3235
{
36+
private static final Log LOG = LogFactory.getLog(RandomAccessReadDataStream.class);
37+
3338
private final long length;
3439
private final byte[] data;
3540
private int currentPosition = 0;
@@ -174,6 +179,20 @@ public int read(byte[] b, int off, int len) throws IOException
174179
return bytesToRead;
175180
}
176181

182+
@Override
183+
public RandomAccessRead createSubView(long length)
184+
{
185+
try
186+
{
187+
return new RandomAccessReadBuffer(data).createView(currentPosition, length);
188+
}
189+
catch (IOException e)
190+
{
191+
LOG.warn("Could not create a SubView", e);
192+
return null;
193+
}
194+
}
195+
177196
/**
178197
* {@inheritDoc}
179198
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.fontbox.ttf;
18+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import org.apache.pdfbox.io.RandomAccessRead;
22+
import org.apache.pdfbox.io.RandomAccessReadView;
23+
24+
/**
25+
* In contrast to {@link RandomAccessReadDataStream},
26+
* this class doesn't pre-load {@code RandomAccessRead} into a {@code byte[]},
27+
* it works with {@link RandomAccessRead} directly.
28+
*
29+
* Performance: it is much faster if most of the buffer is skipped, and slower if whole buffer is read()
30+
*/
31+
class RandomAccessReadUnbufferedDataStream extends TTFDataStream
32+
{
33+
private final long length;
34+
private final RandomAccessRead randomAccessRead;
35+
36+
/**
37+
* @throws IOException If there is a problem reading the source length.
38+
*/
39+
RandomAccessReadUnbufferedDataStream(RandomAccessRead randomAccessRead) throws IOException
40+
{
41+
this.length = randomAccessRead.length();
42+
this.randomAccessRead = randomAccessRead;
43+
}
44+
45+
/**
46+
* {@inheritDoc}
47+
*/
48+
@Override
49+
public long getCurrentPosition() throws IOException
50+
{
51+
return randomAccessRead.getPosition();
52+
}
53+
54+
/**
55+
* Close the underlying resources.
56+
*
57+
* @throws IOException If there is an error closing the resources.
58+
*/
59+
@Override
60+
public void close() throws IOException
61+
{
62+
randomAccessRead.close();
63+
}
64+
65+
/**
66+
* {@inheritDoc}
67+
*/
68+
@Override
69+
public int read() throws IOException
70+
{
71+
return randomAccessRead.read();
72+
}
73+
74+
/**
75+
* {@inheritDoc}
76+
*/
77+
@Override
78+
public final long readLong() throws IOException
79+
{
80+
return ((long) readInt() << 32) | (readInt() & 0xFFFFFFFFL);
81+
}
82+
83+
/**
84+
* {@inheritDoc}
85+
*/
86+
private int readInt() throws IOException
87+
{
88+
int b1 = read();
89+
int b2 = read();
90+
int b3 = read();
91+
int b4 = read();
92+
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
93+
}
94+
95+
/**
96+
* {@inheritDoc}
97+
*/
98+
@Override
99+
public void seek(long pos) throws IOException
100+
{
101+
randomAccessRead.seek(pos);
102+
}
103+
104+
/**
105+
* {@inheritDoc}
106+
*/
107+
@Override
108+
public int read(byte[] b, int off, int len) throws IOException
109+
{
110+
return randomAccessRead.read(b, off, len);
111+
}
112+
113+
/**
114+
* Lifetime of returned InputStream is bound by {@code this} lifetime, it won't close underlying {@code RandomAccessRead}.
115+
*
116+
* {@inheritDoc}
117+
*/
118+
@Override
119+
public InputStream getOriginalData() throws IOException
120+
{
121+
return new RandomAccessReadNonClosingInputStream(randomAccessRead.createView(0, length));
122+
}
123+
124+
/**
125+
* {@inheritDoc}
126+
*/
127+
@Override
128+
public long getOriginalDataSize()
129+
{
130+
return length;
131+
}
132+
133+
@Override
134+
public RandomAccessRead createSubView(long length)
135+
{
136+
try
137+
{
138+
return randomAccessRead.createView(randomAccessRead.getPosition(), length);
139+
}
140+
catch (IOException ex)
141+
{
142+
assert false : "Please implement " + randomAccessRead.getClass() + ".createView()";
143+
return null;
144+
}
145+
}
146+
147+
private static final class RandomAccessReadNonClosingInputStream extends InputStream
148+
{
149+
150+
private final RandomAccessReadView randomAccessRead;
151+
152+
public RandomAccessReadNonClosingInputStream(RandomAccessReadView randomAccessRead)
153+
{
154+
this.randomAccessRead = randomAccessRead;
155+
}
156+
157+
@Override
158+
public int read() throws IOException
159+
{
160+
return randomAccessRead.read();
161+
}
162+
163+
@Override
164+
public int read(byte[] b) throws IOException
165+
{
166+
return randomAccessRead.read(b);
167+
}
168+
169+
@Override
170+
public int read(byte[] b, int off, int len) throws IOException
171+
{
172+
return randomAccessRead.read(b, off, len);
173+
}
174+
175+
@Override
176+
public long skip(long n) throws IOException
177+
{
178+
randomAccessRead.seek(randomAccessRead.getPosition() + n);
179+
return n;
180+
}
181+
182+
@Override
183+
public void close() throws IOException
184+
{
185+
// WARNING: .close() will close RandomAccessReadMemoryMappedFile if this View was based on it
186+
// randomAccessRead.close();
187+
}
188+
}
189+
}

fontbox/src/main/java/org/apache/fontbox/ttf/TTCDataStream.java

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.io.IOException;
2121
import java.io.InputStream;
22+
import org.apache.pdfbox.io.RandomAccessRead;
2223

2324
/**
2425
* A wrapper for a TTF stream inside a TTC file, does not close the underlying shared stream.
@@ -83,4 +84,9 @@ public long getOriginalDataSize()
8384
return stream.getOriginalDataSize();
8485
}
8586

87+
@Override
88+
public RandomAccessRead createSubView(long length)
89+
{
90+
return stream.createSubView(length);
91+
}
8692
}

fontbox/src/main/java/org/apache/fontbox/ttf/TTFDataStream.java

+12
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.nio.charset.StandardCharsets;
2525
import java.util.Calendar;
2626
import java.util.TimeZone;
27+
import org.apache.pdfbox.io.RandomAccessRead;
2728

2829
/**
2930
* An abstract class to read a data stream.
@@ -279,6 +280,17 @@ public byte[] read(int numberOfBytes) throws IOException
279280
*/
280281
public abstract int read(byte[] b, int off, int len) throws IOException;
281282

283+
/**
284+
* Creates a view from current position to {@code pos + length}.
285+
* It can be faster than {@code read(length)} if you only need a few bytes.
286+
* {@code SubView.close()} should never close {@code TTFDataStream.this}, only itself.
287+
*
288+
* @return A view or null (caller can use {@link #read} instead). Please close() the result
289+
*/
290+
public RandomAccessRead createSubView(long length) {
291+
return null;
292+
}
293+
282294
/**
283295
* Get the current position in the stream.
284296
*

0 commit comments

Comments
 (0)