// Copyright 2016 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fpdfapi/render/charposlist.h" #include "build/build_config.h" #include "core/fpdfapi/font/cpdf_cidfont.h" #include "core/fpdfapi/font/cpdf_font.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fxge/cfx_fontmapper.h" #include "core/fxge/cfx_substfont.h" #include "core/fxge/text_char_pos.h" namespace { bool ShouldUseExistingFont(const CPDF_Font* font, uint32_t glyph_id, bool has_to_unicode) { // Check for invalid glyph ID. if (glyph_id == static_cast(-1)) return false; if (!font->IsTrueTypeFont()) return true; // For TrueType fonts, a glyph ID of 0 may be invalid. // // When a "ToUnicode" entry exists in the font dictionary, it indicates // a "ToUnicode" mapping file is used to convert from CIDs (which // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File // Tutorial - Adobe // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf // and // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6) return glyph_id != 0 || has_to_unicode; } // The following is not a perfect solution and can be further improved. // For example, if `subst_font` is "Book" and the `base_font_name` is "Bookman", // this function will return "true" even though the actual font "Bookman" // is not loaded. // An exact string match is not possible here, because `subst_font_name` // will be the same value for different postscript names. // For example: "Times New Roman" as `subst_font_name` for all of these // `base_font_name` values: "TimesNewRoman,Bold", "TimesNewRomanPS-Bold", // "TimesNewRomanBold" and "TimesNewRoman-Bold". bool IsActualFontLoaded(const CFX_SubstFont* subst_font, const ByteString& base_font_name) { // Skip if we loaded the actual font. // example: TimesNewRoman,Bold -> Times New Roman ByteString subst_font_name = subst_font->m_Family; subst_font_name.Remove(' '); subst_font_name.MakeLower(); absl::optional find = base_font_name.Find(subst_font_name.AsStringView()); return find.has_value() && find.value() == 0; } bool ApplyGlyphSpacingHeuristic(const CPDF_Font* font, const CFX_Font* current_font, bool is_vertical_writing) { if (is_vertical_writing || font->IsEmbedded() || !font->HasFontWidths()) { return false; } // Skip if we loaded a standard alternate font. // example: Helvetica -> Arial ByteString base_font_name = font->GetBaseFontName(); base_font_name.MakeLower(); auto standard_font_name = CFX_FontMapper::GetStandardFontName(&base_font_name); if (standard_font_name.has_value()) { return false; } CFX_SubstFont* subst_font = current_font->GetSubstFont(); if (subst_font->IsBuiltInGenericFont()) { return false; } return !IsActualFontLoaded(subst_font, base_font_name); } } // namespace std::vector GetCharPosList(pdfium::span char_codes, pdfium::span char_pos, CPDF_Font* font, float font_size) { std::vector results; results.reserve(char_codes.size()); CPDF_CIDFont* cid_font = font->AsCIDFont(); bool is_vertical_writing = cid_font && cid_font->IsVertWriting(); bool has_to_unicode = !!font->GetFontDict()->GetStreamFor("ToUnicode"); for (size_t i = 0; i < char_codes.size(); ++i) { uint32_t char_code = char_codes[i]; if (char_code == static_cast(-1)) continue; bool is_vertical_glyph = false; results.emplace_back(); TextCharPos& text_char_pos = results.back(); if (cid_font) text_char_pos.m_bFontStyle = true; WideString unicode = font->UnicodeFromCharCode(char_code); text_char_pos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : char_code; text_char_pos.m_GlyphIndex = font->GlyphFromCharCode(char_code, &is_vertical_glyph); uint32_t glyph_id = text_char_pos.m_GlyphIndex; #if BUILDFLAG(IS_APPLE) text_char_pos.m_ExtGID = font->GlyphFromCharCodeExt(char_code); glyph_id = text_char_pos.m_ExtGID != static_cast(-1) ? text_char_pos.m_ExtGID : text_char_pos.m_GlyphIndex; #endif CFX_Font* current_font; if (ShouldUseExistingFont(font, glyph_id, has_to_unicode)) { current_font = font->GetFont(); text_char_pos.m_FallbackFontPosition = -1; } else { int32_t fallback_position = font->FallbackFontFromCharcode(char_code); current_font = font->GetFontFallback(fallback_position); text_char_pos.m_FallbackFontPosition = fallback_position; text_char_pos.m_GlyphIndex = font->FallbackGlyphFromCharcode(fallback_position, char_code); #if BUILDFLAG(IS_APPLE) text_char_pos.m_ExtGID = text_char_pos.m_GlyphIndex; #endif } if (!font->IsEmbedded() && !font->IsCIDFont()) text_char_pos.m_FontCharWidth = font->GetCharWidthF(char_code); else text_char_pos.m_FontCharWidth = 0; text_char_pos.m_Origin = CFX_PointF(i > 0 ? char_pos[i - 1] : 0, 0); text_char_pos.m_bGlyphAdjust = false; float scaling_factor = 1.0f; if (ApplyGlyphSpacingHeuristic(font, current_font, is_vertical_writing)) { int pdf_glyph_width = font->GetCharWidthF(char_code); int font_glyph_width = current_font->GetGlyphWidth(text_char_pos.m_GlyphIndex); if (font_glyph_width && pdf_glyph_width > font_glyph_width + 1) { // Move the initial x position by half of the excess (transformed to // text space coordinates). text_char_pos.m_Origin.x += (pdf_glyph_width - font_glyph_width) * font_size / 2000.0f; } else if (pdf_glyph_width && font_glyph_width && pdf_glyph_width < font_glyph_width) { scaling_factor = static_cast(pdf_glyph_width) / font_glyph_width; text_char_pos.m_AdjustMatrix[0] = scaling_factor; text_char_pos.m_AdjustMatrix[1] = 0.0f; text_char_pos.m_AdjustMatrix[2] = 0.0f; text_char_pos.m_AdjustMatrix[3] = 1.0f; text_char_pos.m_bGlyphAdjust = true; } } if (!cid_font) continue; uint16_t cid = cid_font->CIDFromCharCode(char_code); if (is_vertical_writing) { text_char_pos.m_Origin = CFX_PointF(0, text_char_pos.m_Origin.x); CFX_Point16 vertical_origin = cid_font->GetVertOrigin(cid); text_char_pos.m_Origin.x -= font_size * vertical_origin.x / 1000; text_char_pos.m_Origin.y -= font_size * vertical_origin.y / 1000; } const uint8_t* cid_transform = cid_font->GetCIDTransform(cid); if (cid_transform && !is_vertical_glyph) { text_char_pos.m_AdjustMatrix[0] = cid_font->CIDTransformToFloat(cid_transform[0]) * scaling_factor; text_char_pos.m_AdjustMatrix[1] = cid_font->CIDTransformToFloat(cid_transform[1]) * scaling_factor; text_char_pos.m_AdjustMatrix[2] = cid_font->CIDTransformToFloat(cid_transform[2]); text_char_pos.m_AdjustMatrix[3] = cid_font->CIDTransformToFloat(cid_transform[3]); text_char_pos.m_Origin.x += cid_font->CIDTransformToFloat(cid_transform[4]) * font_size; text_char_pos.m_Origin.y += cid_font->CIDTransformToFloat(cid_transform[5]) * font_size; text_char_pos.m_bGlyphAdjust = true; } } return results; }