Type3 fonts and how to get the minimum bounding box for each character

Q:
I am attempting to get the minimum bounding box of a Text Element. The
following code throws an exception when calling GetUnitsPerEm(). Can
you shed some light as to why it might be crashing?

The document enclosed has the problem on the very first Text Element.
The Element's font type is Type3.

bool MyClass:GetTextElementBB(Element& element, Rect* pBbox,
CharIterator& startChar, CharIterator& endChar, const double fontSize)
{
    bool returnValue;
    std::vector <char> path_oprs;
    std::vector <pdftron::Real> path_data;

    GState* gs = element.GetGState();
    Font& font = gs->GetFont();

    double font_size = gs->GetFontSize();
    double horiz_spacing = gs->GetHorizontalScale() / 100.0;
    Matrix2D font_mtx(font_size * horiz_spacing, 0, 0, font_size, 0,
0);

    double units_per_em = font.GetUnitsPerEm();
    font_mtx *= Matrix2D(1.0/units_per_em, 0, 0, -1.0/units_per_em, 0,
0);

    Matrix2D pos(1, 0, 0, 1, (*startChar).x, (*startChar).y); //
CharIterator positioning info.
    Matrix2D pos2(1, 0, 0, 1, (*endChar).x, (*endChar).y); //
CharIterator positioning info.

    Matrix2D path_mtx(element.GetCTM() * element.GetTextMatrix() * pos
* font_mtx);
    Matrix2D path_mtx2(element.GetCTM() * element.GetTextMatrix() *
pos2 * font_mtx);

    if (font.GetGlyphPath(endChar->char_code, (std::vector
<pdftron::UChar>& )path_oprs, path_data, true, &path_mtx2) == false)
    {
        return false;
    }

    if (!path_oprs.empty() && !path_data.empty())
    {
        returnValue = GetPathBBox(&path_data[0], (int)path_data.size(),
(char*) &path_oprs[0],
               (int)path_oprs.size(), pBbox->x1, pBbox->y1, pBbox->x2,
pBbox->y2);
    }

    pBbox->x1 = path_mtx.m_h;

    pBbox->y1 = path_mtx.m_v;
    pBbox->y2 = pBbox->y1 + (fontSize / fontSizeToHeightFactor);

    m_middleX = pBbox->x1 + (pBbox->Width() / 2.0);
    m_middleY = pBbox->y1 + (pBbox->Height() / 2.0);

    return true;
}

bool Area::GetPathBBox(const double* data, int data_sz, const char*
opr, int opr_sz,
                            double &min_x, double &min_y, double
&max_x, double &max_y)
{
    const char *opr_itr = opr, *opr_end = opr + opr_sz;
    if (opr_itr >= opr_end) return false;

    const double *data_itr = data, *data_end = data + data_sz;
    double x, y;
    static const double invalid_value = 1e300;
    max_x = max_y = -invalid_value;
    min_x = min_y = invalid_value;
    for (; opr_itr<opr_end; ++opr_itr)
    {
        switch(*opr_itr)
        {
        case Element::e_moveto:
            if (!(data_itr+2<=data_end)) return false;
            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;
            break;
        case Element::e_lineto:
            if (!(data_itr+2<=data_end)) return false;
            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            break;
        case Element::e_rect:
            if (!(data_itr+4<=data_end)) return false;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            x = x + *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = y + *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            break;
        case Element::e_cubicto:
            if (!(data_itr+6<=data_end)) return false;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            break;
        case Element::e_conicto:
            if (!(data_itr+4<=data_end)) return false;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            x = *data_itr; ++data_itr;
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;

            y = *data_itr; ++data_itr;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;

            break;
        }
    }

    return (min_x != invalid_value && min_y != invalid_value);
}
----------------------
A:

GetUnitsPerEm() does not appy to type3 fonts (see PDFNet API Reference
documentation or 'Font.h' header file).
To extract Type3 fonts you wold need to use ElementReader (similar to
extraction of page content).

Please note that Type3 fonts are 'mini' PDF pages and can reference
images and other fonts beside vector paths.

As a result, you will need special code to handle text with type3
fonts.
For example:

CharIterator itr = element->CharBegin();
CharIterator end = element->CharEnd();
if (itr == end) return;

GState* gs = element->GetGState();
PDF::Font font = gs->GetFont();

double font_size = gs->GetFontSize();
double horiz_spacing = gs->GetHorizontalScale() / 100.0;
Matrix2D font_mtx(font_size * horiz_spacing, 0, 0, font_size, 0, 0);
const Matrix2D& ctm = element->GetCTM();

if (font.GetType() == Font::e_Type3) {
  Matrix2D prev_mtx(mtx * ctm * element->GetTextMatrix());
  font_mtx *= font.GetType3FontMatrix();
  for (; itr!=end; ++itr) {
  if (SDF::Obj* font_stm = font.GetType3GlyphStream(itr->char_code)) {
    Matrix2D pos(1, 0, 0, 1, itr->x, itr->y);
    Matrix2D gl_mtx(prev_mtx * pos * font_mtx);
    mp_reader->Type3FontBegin(font_stm, font.GetSDFObj());
    ProcessElements(gl_mtx, element_reader);
    mp_reader->End();
    }
  }
}

ProcessElements could be the same recursive function that you use to
read page contet.