Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
private int firstUnmapped;
private int lastUnmapped;

/** Contains the character bounding boxes for all characters in the font */
protected Rectangle[] boundingBoxes;
/** Contains the character bounding boxes for all characters in the font,
* packed as x, y, width, height per glyph. A Rectangle per glyph costs 36
* bytes (32 for the object, 4 for the array slot) where these cost 16, and
* the array is retained for as long as the font is loaded. */
protected int[] boundingBoxes;

private boolean isOTFFile;

Expand Down Expand Up @@ -210,8 +213,9 @@ public int[] getWidths() {

public Rectangle getBoundingBox(int glyphIndex, int size) {
int index = isEmbeddable() ? cidSet.getOriginalGlyphIndex(glyphIndex) : glyphIndex;
Rectangle bbox = boundingBoxes[index];
return new Rectangle(bbox.x * size, bbox.y * size, bbox.width * size, bbox.height * size);
int i = index * 4;
return new Rectangle(boundingBoxes[i] * size, boundingBoxes[i + 1] * size,
boundingBoxes[i + 2] * size, boundingBoxes[i + 3] * size);
}

/**
Expand Down Expand Up @@ -451,6 +455,24 @@ public void setWidthArray(int[] wds) {
* @param boundingBoxes array of bounding boxes.
*/
public void setBBoxArray(Rectangle[] boundingBoxes) {
int[] packed = new int[boundingBoxes.length * 4];
for (int i = 0; i < boundingBoxes.length; i++) {
Rectangle bbox = boundingBoxes[i];
packed[i * 4] = bbox.x;
packed[i * 4 + 1] = bbox.y;
packed[i * 4 + 2] = bbox.width;
packed[i * 4 + 3] = bbox.height;
}
this.boundingBoxes = packed;
}

/**
* Sets the bounding boxes array, packed as x, y, width, height per glyph.
* Preferred over setBBoxArray(Rectangle[]), which allocates a Rectangle per
* glyph merely to pass the values in.
* @param boundingBoxes packed bounding boxes, 4 ints per glyph.
*/
public void setBBoxArray(int[] boundingBoxes) {
this.boundingBoxes = boundingBoxes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ private void buildFont(OpenFont otf, String ttcFontName) {
multiFont.setCIDType(CIDFontType.CIDTYPE2);
}
multiFont.setWidthArray(otf.getWidths());
multiFont.setBBoxArray(otf.getBoundingBoxes());
multiFont.setBBoxArray(otf.getBoundingBoxesPacked());
} else {
singleFont.setFontType(FontType.TRUETYPE);
singleFont.setEncoding(otf.getCharSetName());
Expand Down
17 changes: 17 additions & 0 deletions fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,23 @@ public int[] getWidths() {
return wx;
}

/**
* The bounding boxes of all glyphs, packed as x, y, width, height per glyph
* (so 4 ints each). The same values as getBoundingBoxes(), without a
* Rectangle per glyph.
*/
public int[] getBoundingBoxesPacked() {
int[] packed = new int[mtxTab.length * 4];
for (int i = 0; i < mtxTab.length; i++) {
int[] boundingBox = mtxTab[i].getBoundingBox();
packed[i * 4] = convertTTFUnit2PDFUnit(boundingBox[0]);
packed[i * 4 + 1] = convertTTFUnit2PDFUnit(boundingBox[1]);
packed[i * 4 + 2] = convertTTFUnit2PDFUnit(boundingBox[2] - boundingBox[0]);
packed[i * 4 + 3] = convertTTFUnit2PDFUnit(boundingBox[3] - boundingBox[1]);
}
return packed;
}

public Rectangle[] getBoundingBoxes() {
Rectangle[] boundingBoxes = new Rectangle[mtxTab.length];
for (int i = 0; i < boundingBoxes.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.awt.Rectangle;

import org.junit.Test;
import static org.junit.Assert.assertEquals;

/**
* Tests for MultiByteFont's glyph bounding boxes, which are stored packed as
* ints rather than as a Rectangle per glyph.
*/
public class MultiByteFontTestCase {

/** Not embeddable (there is no embed URI), so bounding boxes are indexed by glyph index. */
private MultiByteFont font() {
return new MultiByteFont(null, EmbeddingMode.AUTO);
}

@Test
public void testPackedBBoxArray() {
MultiByteFont font = font();
font.setBBoxArray(new int[] {1, 2, 3, 4, 5, 6, 7, 8});

assertEquals(new Rectangle(1, 2, 3, 4), font.getBoundingBox(0, 1));
assertEquals(new Rectangle(5, 6, 7, 8), font.getBoundingBox(1, 1));
}

@Test
public void testRectangleBBoxArray() {
MultiByteFont font = font();
font.setBBoxArray(new Rectangle[] {new Rectangle(1, 2, 3, 4), new Rectangle(5, 6, 7, 8)});

assertEquals(new Rectangle(1, 2, 3, 4), font.getBoundingBox(0, 1));
assertEquals(new Rectangle(5, 6, 7, 8), font.getBoundingBox(1, 1));
}

@Test
public void testBoundingBoxIsScaledBySize() {
MultiByteFont font = font();
font.setBBoxArray(new int[] {-1, 2, 3, 4});

assertEquals(new Rectangle(-10, 20, 30, 40), font.getBoundingBox(0, 10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.apache.fop.fonts.truetype;

import java.awt.Rectangle;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
Expand Down Expand Up @@ -577,6 +578,24 @@ public void testBBox() {
assertEquals(dejavuTTFFile.getBBox(2330).length, 4);
}

/**
* Test getBoundingBoxesPacked() - it must carry the same values as
* getBoundingBoxes(), which it exists to avoid allocating.
*/
@Test
public void testGetBoundingBoxesPacked() {
Rectangle[] boundingBoxes = dejavuTTFFile.getBoundingBoxes();
int[] packed = dejavuTTFFile.getBoundingBoxesPacked();
assertEquals(boundingBoxes.length * 4, packed.length);
for (int i = 0; i < boundingBoxes.length; i++) {
Rectangle boundingBox = boundingBoxes[i];
assertEquals(boundingBox.x, packed[i * 4]);
assertEquals(boundingBox.y, packed[i * 4 + 1]);
assertEquals(boundingBox.width, packed[i * 4 + 2]);
assertEquals(boundingBox.height, packed[i * 4 + 3]);
}
}

@Test
public void testReservedIndex() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Expand Down