Sample Code On Leveraging Text/Font Vector Data from Font::GetGlyphPath

The following function definition replaces the ProcessElements function in the ElementEdit sample:

https://www.pdftron.com/documentation/samples/cpp/ElementEditTest

static void ProcessElements(ElementReader& reader, ElementWriter& writer, XObjSet& visited)
{
Element element;
while (element = reader.Next()) // Read page contents
{
switch (element.GetType())
{
case Element::e_text_begin:
case Element::e_text_end:
break;
case Element::e_text:
{
Matrix2D ctm = element.GetCTM();
GState gs = element.GetGState();
double fontSize = gs.GetFontSize();
Font font = gs.GetFont();
double hscale = gs.GetHorizontalScale() / 100.0;
Matrix2D textMtx = element.GetTextMatrix();
Matrix2D posMtx = Matrix2D(1, 0, 0, 1, 0, 0);
Matrix2D fontMtx = Matrix2D(fontSize * hscale, 0, 0, fontSize, 0, 0);
double unitsPerEm = font.GetUnitsPerEm();
Matrix2D unitsPerEmMtx = Matrix2D(1.0 / unitsPerEm, 0, 0, -1.0 / unitsPerEm, 0, 0);
fontMtx = fontMtx * unitsPerEmMtx;
for (CharIterator citr = element.GetCharIterator(); citr.HasNext(); citr.Next())
{
CharData charData = citr.Current();
posMtx.m_h = charData.x;
posMtx.m_v = charData.y;
Matrix2D pathMtx = ctm * textMtx * posMtx * fontMtx;
PathData pathData = font.GetGlyphPath(charData.char_code, true);
if (pathData.GetOperators().size() > 0 && pathData.GetPoints().size() > 0)
{
vector points = pathData.GetPoints();
for (int i = 0; i < points.size(); i += 2)
{
double x = points[i];
double y = points[i + 1];
Point translatedPoint = pathMtx.Mult(Point(x, y));
points[i] = translatedPoint.x;
points[i + 1] = translatedPoint.y;
}
pathData.SetPoints(points);
ElementBuilder builder;
Element pathElement = builder.CreatePath(pathData.GetPoints(), pathData.GetOperators());
pathElement.SetPathFill(true);
pathElement.SetPathStroke(false);
pathElement.GetGState().SetFillColorSpace(ColorSpace::CreateDeviceGray());
pathElement.GetGState().SetFillColor(ColorPt(0.5, 0.0, 0.0));
writer.WritePlacedElement(pathElement);
}
}
break;
}
case Element::e_form:
{
writer.WriteElement(element); // write Form XObject reference to current stream

Obj form_obj = element.GetXObject();
if (visited.find(form_obj.GetObjNum()) == visited.end()) // if this XObject has not been processed
{
// recursively process the Form XObject
visited.insert(form_obj.GetObjNum());
ElementWriter new_writer;
reader.FormBegin();
new_writer.Begin(form_obj);

reader.ClearChangeList();
new_writer.SetDefaultGState(reader);

ProcessElements(reader, new_writer, visited);
new_writer.End();
reader.End();
}
break;
}
default:
writer.WriteElement(element);
}
}
}

The full sample can be executed by using the attached ElementEditTest.cpp file and replacing it with the default ElementEditTest sample found in the PDFNet download:

https://www.pdftron.com/documentation/cpp/get-started/integration/

The Java equivalent of the Element::e_text case has been attached as well, noting that operator overloads do not exist in the Java version PDFNet, hence the need for calling explicit matrix multiplication functions built-in to the Matrix2D class.

ElementEditTest.cpp (5.08 KB)

text2vector.java (1.66 KB)