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
32 changes: 23 additions & 9 deletions pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,17 @@ public Matrix getFontMatrix()
return DEFAULT_FONT_MATRIX;
}

/**
* Whether {@link #encode(int)} derives codes from a substitute font program rather than from
* this font's encoding, so that the codes need not exist in the PDF.
*
* @return true if encoded codes depend on the installed substitute
*/
protected boolean isEncodedViaSubstitute()
{
return false;
}

/**
* Determines the width of the space character. This is very important for text extraction.
*
Expand All @@ -596,16 +607,19 @@ public float getSpaceWidth()
}
else
{
try
if (!isEncodedViaSubstitute())
{
// PDFBOX-5920: try with encoding, which gets the correct code
fontWidthOfSpace = getStringWidth(" ");
}
catch (IllegalArgumentException | UnsupportedOperationException ex)
{
// Happens if space is not available in the font
// or if encoding isn't implemented
LOG.debug(ex.getMessage(), ex);
try
{
// PDFBOX-5920: try with encoding, which gets the correct code
fontWidthOfSpace = getStringWidth(" ");
}
catch (IllegalArgumentException | UnsupportedOperationException ex)
{
// Happens if space is not available in the font
// or if encoding isn't implemented
LOG.debug(ex.getMessage(), ex);
}
}
if (fontWidthOfSpace <= 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,15 @@ public float getWidthFromFont(int code) throws IOException
return descendantFont.getWidthFromFont(code, this);
}

@Override
protected boolean isEncodedViaSubstitute()
{
// Identity CMap without ToUnicode: encode() returns the substitute's GIDs (PDFBOX-6292)
return !isEmbedded() && descendantFont instanceof PDCIDFontType2 &&
cMap != null && cMap.getName() != null && cMap.getName().startsWith("Identity-") &&
!getCOSObject().containsKey(COSName.TO_UNICODE);
}

@Override
public boolean isEmbedded()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.
*/
package org.apache.pdfbox.pdmodel.font;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import java.io.IOException;

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.junit.jupiter.api.Test;

/**
* The space width of a non-embedded Identity-H CIDFontType2 without ToUnicode must come from the
* PDF, not from whichever substitute is installed. PDFBOX-6292.
*/
class SubstituteSpaceWidthTest
{
@Test
void testSpaceWidthIndependentOfSubstitute() throws IOException
{
PDType0Font font = new PDType0Font(simSunIdentityH(), null);
PDCIDFontType2 cidFont = (PDCIDFontType2) font.getDescendantFont();
// any substitute works, down to the bundled last-resort font, as long as its space GID
// lands in the /W range below
int substituteSpaceGid = cidFont.getTrueTypeFont().getUnicodeCmapLookup().getGlyphId(' ');
assumeTrue(substituteSpaceGid > 0 && substituteSpaceGid < 32,
"substitute's space GID outside the test's /W range");

// an Identity CMap without ToUnicode says nothing about which code is a space; the answer
// must not depend on which substitute happens to be installed
assertEquals(font.getWidth(32), font.getSpaceWidth());
}

private static COSDictionary simSunIdentityH()
{
COSDictionary ros = new COSDictionary();
ros.setItem(COSName.REGISTRY, new COSString("Adobe"));
ros.setItem(COSName.ORDERING, new COSString("GB1"));
ros.setInt(COSName.SUPPLEMENT, 2);

COSDictionary fd = new COSDictionary();
fd.setItem(COSName.TYPE, COSName.FONT_DESC);
fd.setName(COSName.FONT_NAME, "SimSun");
fd.setInt(COSName.FLAGS, 6);
COSArray bbox = new COSArray();
bbox.add(COSInteger.get(0));
bbox.add(COSInteger.get(-141));
bbox.add(COSInteger.get(996));
bbox.add(COSInteger.get(855));
fd.setItem(COSName.FONT_BBOX, bbox);
fd.setInt(COSName.ITALIC_ANGLE, 0);
fd.setInt(COSName.ASCENT, 859);
fd.setInt(COSName.DESCENT, -141);
fd.setInt(COSName.CAP_HEIGHT, 859);
fd.setInt(COSName.STEM_V, 80);

// widths for codes 0-31 only: the substitute's space GID lands here if it is wrongly
// used as a code, while the PDF's code 32 falls back to /DW
COSArray widths = new COSArray();
for (int i = 0; i < 32; i++)
{
widths.add(COSInteger.get(500));
}
COSArray w = new COSArray();
w.add(COSInteger.get(0));
w.add(widths);

COSDictionary cid = new COSDictionary();
cid.setItem(COSName.TYPE, COSName.FONT);
cid.setItem(COSName.SUBTYPE, COSName.CID_FONT_TYPE2);
cid.setName(COSName.BASE_FONT, "SimSun");
cid.setItem(COSName.CIDSYSTEMINFO, ros);
cid.setItem(COSName.FONT_DESC, fd);
cid.setInt(COSName.DW, 1000);
cid.setItem(COSName.W, w);

COSArray descendants = new COSArray();
descendants.add(cid);
COSDictionary type0 = new COSDictionary();
type0.setItem(COSName.TYPE, COSName.FONT);
type0.setItem(COSName.SUBTYPE, COSName.TYPE0);
type0.setName(COSName.BASE_FONT, "SimSun");
type0.setName(COSName.ENCODING, "Identity-H");
type0.setItem(COSName.DESCENDANT_FONTS, descendants);
return type0;
}
}
Loading