0

Embed fonts information into resulting PDF file for printing.

BUG=9847
TEST=printing on linux should have right font in pdf

Patch contributed by Min-Yu Huang <minyu.huang@gmail.com>
Review URL: http://codereview.chromium.org/196071

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25974 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
maruel@chromium.org
2009-09-11 14:19:53 +00:00
parent c10cdbdd32
commit 923fa31b68
3 changed files with 207 additions and 40 deletions

@ -7,14 +7,62 @@
#include <stdio.h>
#include <cairo.h>
#include <cairo-ft.h>
#include <cairo-pdf.h>
#include <cairo-ps.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <map>
#include "base/file_util.h"
#include "base/logging.h"
#include "base/singleton.h"
#include "third_party/skia/include/core/SkFontHost.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace {
FT_Library g_ft_library = NULL; // handle to FreeType library.
struct FontInfo {
SkStream* font_stream;
FT_Face ft_face;
cairo_font_face_t* cairo_face;
cairo_user_data_key_t data_key;
};
typedef std::map<uint32_t, FontInfo> MapFontId2FontInfo;
// NOTE: Only call this function when no further rendering will be performed,
// and/or the metafile is closed.
void CleanUpFonts() {
MapFontId2FontInfo* g_font_cache = Singleton<MapFontId2FontInfo>::get();
DCHECK(g_font_cache);
for (MapFontId2FontInfo::iterator it = g_font_cache->begin();
it !=g_font_cache->end();
++it) {
DCHECK(it->second.cairo_face);
DCHECK(it->second.font_stream);
cairo_font_face_destroy(it->second.cairo_face);
// |it->second.ft_face| is handled by Cairo.
it->second.font_stream->unref();
}
g_font_cache->clear();
}
void CleanUpFreeType() {
if (g_ft_library) {
FT_Error ft_error = FT_Done_FreeType(g_ft_library);
g_ft_library = NULL;
DCHECK_EQ(ft_error, 0);
}
}
// Tests if |surface| is valid.
bool IsSurfaceValid(cairo_surface_t* surface) {
return cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS;
@ -48,7 +96,7 @@ cairo_status_t WriteCairoStream(void* dst_buffer,
unsigned int src_data_length) {
DCHECK(dst_buffer);
DCHECK(src_data);
DCHECK(src_data_length > 0);
DCHECK_GT(src_data_length, 0u);
std::string* buffer = reinterpret_cast<std::string*>(dst_buffer);
buffer->append(reinterpret_cast<const char*>(src_data), src_data_length);
@ -67,8 +115,8 @@ PdfPsMetafile::PdfPsMetafile(const FileFormat& format)
}
PdfPsMetafile::~PdfPsMetafile() {
// Releases resources if we forgot to do so.
CleanUp();
// Releases all resources if we forgot to do so.
CleanUpAll();
}
bool PdfPsMetafile::Init() {
@ -77,8 +125,17 @@ bool PdfPsMetafile::Init() {
// page_surface_, and page_context_ are NULL, and current_page_ is empty.
DCHECK(!context_);
DCHECK(all_pages_.empty());
DCHECK(!g_ft_library);
// Create an 1 by 1 Cairo surface for entire PDF/PS file.
// Initializes FreeType library.
FT_Error ft_error = FT_Init_FreeType(&g_ft_library);
if (ft_error) {
DLOG(ERROR) << "Cannot initialize FreeType library for PdfPsMetafile.";
g_ft_library = NULL;
return false;
}
// Creates an 1 by 1 Cairo surface for entire PDF/PS file.
// The size for each page will be overwritten later in StartPage().
switch (format_) {
case PDF: {
@ -103,15 +160,17 @@ bool PdfPsMetafile::Init() {
if (!IsSurfaceValid(surface_)) {
DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!";
CleanUpSurface(&surface_);
CleanUpFreeType();
return false;
}
// Create a context.
// Creates a context.
context_ = cairo_create(surface_);
if (!IsContextValid(context_)) {
DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!";
CleanUpContext(&context_);
CleanUpSurface(&surface_);
CleanUpFreeType();
return false;
}
@ -145,7 +204,7 @@ cairo_t* PdfPsMetafile::StartPage(double width_in_points,
DCHECK_GT(width_in_points, 0.);
DCHECK_GT(height_in_points, 0.);
// Create a target surface for the new page.
// Creates a target surface for the new page.
// Cairo 1.6.0 does NOT allow the first argument be NULL,
// but some newer versions do support NULL pointer.
switch (format_) {
@ -167,7 +226,7 @@ cairo_t* PdfPsMetafile::StartPage(double width_in_points,
default:
NOTREACHED();
CleanUp();
CleanUpAll();
return NULL;
}
@ -175,15 +234,15 @@ cairo_t* PdfPsMetafile::StartPage(double width_in_points,
// Hence, we have to check if it points to a "nil" object.
if (!IsSurfaceValid(page_surface_)) {
DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!";
CleanUp();
CleanUpAll();
return NULL;
}
// Create a context.
// Creates a context.
page_context_ = cairo_create(page_surface_);
if (!IsContextValid(page_context_)) {
DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!";
CleanUp();
CleanUpAll();
return NULL;
}
@ -195,9 +254,9 @@ bool PdfPsMetafile::FinishPage(float shrink) {
DCHECK(IsContextValid(context_));
DCHECK(IsSurfaceValid(page_surface_));
DCHECK(IsContextValid(page_context_));
DCHECK(shrink > 0);
DCHECK_GT(shrink, 0);
// Flush all rendering for current page.
// Flushes all rendering for current page.
cairo_surface_flush(page_surface_);
// TODO(myhuang): Use real page settings.
@ -226,20 +285,20 @@ bool PdfPsMetafile::FinishPage(float shrink) {
default:
NOTREACHED();
CleanUp();
CleanUpAll();
return false;
}
// Check if our surface is still valid after resizing.
// Checks if our surface is still valid after resizing.
if (!IsSurfaceValid(surface_)) {
DLOG(ERROR) << "Cannot resize Cairo surface for PdfPsMetafile!";
CleanUp();
CleanUpAll();
return false;
}
// Save context's states.
// Saves context's states.
cairo_save(context_);
// Copy current page onto the surface of final result.
// Copies current page onto the surface of final result.
// Margins are done by coordinates transformation.
// Please NOTE that we have to call cairo_scale() before we call
// cairo_set_source_surface().
@ -275,16 +334,16 @@ bool PdfPsMetafile::FinishPage(float shrink) {
kScaledPrintableHeightInPoint);
cairo_fill(context_);
// Finishing the duplication of current page.
// Finishes the duplication of current page.
cairo_show_page(context_);
cairo_surface_flush(surface_);
// Destroy resoreces for current page.
// Destroys resources for current page.
CleanUpContext(&page_context_);
CleanUpSurface(&page_surface_);
current_page_.clear();
// Restore context's states.
// Restores context's states.
cairo_restore(context_);
return true;
@ -302,6 +361,83 @@ void PdfPsMetafile::Close() {
CleanUpContext(&context_);
CleanUpSurface(&surface_);
CleanUpFonts();
CleanUpFreeType();
}
// static
bool PdfPsMetafile::SelectFontById(cairo_t* context, uint32_t font_id) {
DCHECK(IsContextValid(context));
DCHECK(SkFontHost::ValidFontID(font_id));
DCHECK(g_ft_library);
// Checks if we have a cache hit.
MapFontId2FontInfo* g_font_cache = Singleton<MapFontId2FontInfo>::get();
DCHECK(g_font_cache);
MapFontId2FontInfo::iterator it = g_font_cache->find(font_id);
if (it != g_font_cache->end()) {
cairo_set_font_face(context, it->second.cairo_face);
if (IsContextValid(context)) {
return true;
} else {
NOTREACHED() << "Cannot set font face in Cairo!";
return false;
}
}
// Cache missed. We need to load and create the font.
FontInfo new_font_info = {0};
new_font_info.font_stream = SkFontHost::OpenStream(font_id);
DCHECK(new_font_info.font_stream);
size_t stream_size = new_font_info.font_stream->getLength();
DCHECK(stream_size) << "The Font stream has nothing!";
FT_Error ft_error = FT_New_Memory_Face(
g_ft_library,
static_cast<FT_Byte*>(
const_cast<void*>(new_font_info.font_stream->getMemoryBase())),
stream_size,
0,
&new_font_info.ft_face);
if (ft_error) {
new_font_info.font_stream->unref();
DLOG(ERROR) << "Cannot create FT_Face!";
SkASSERT(false);
return false;
}
new_font_info.cairo_face = cairo_ft_font_face_create_for_ft_face(
new_font_info.ft_face, 0);
DCHECK(new_font_info.cairo_face) << "Cannot create font in Cairo!";
// Manage |new_font_info.ft_face|'s life by Cairo.
cairo_status_t status = cairo_font_face_set_user_data(
new_font_info.cairo_face,
&new_font_info.data_key,
new_font_info.ft_face,
reinterpret_cast<cairo_destroy_func_t>(FT_Done_Face));
if (status != CAIRO_STATUS_SUCCESS) {
DLOG(ERROR) << "Cannot set font's user data in Cairo!";
cairo_font_face_destroy(new_font_info.cairo_face);
FT_Done_Face(new_font_info.ft_face);
new_font_info.font_stream->unref();
SkASSERT(false);
return false;
}
// Inserts |new_font_info| info |g_font_cache|.
(*g_font_cache)[font_id] = new_font_info;
cairo_set_font_face(context, new_font_info.cairo_face);
if (IsContextValid(context)) {
return true;
}
DLOG(ERROR) << "Connot set font face in Cairo!";
return false;
}
unsigned int PdfPsMetafile::GetDataSize() const {
@ -317,7 +453,7 @@ unsigned int PdfPsMetafile::GetDataSize() const {
bool PdfPsMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const {
DCHECK(dst_buffer);
DCHECK(dst_buffer_size > 0);
DCHECK_GT(dst_buffer_size, 0u);
// We need to check at least these two members to ensure that either Init()
// has been called to initialize |all_pages_|, or metafile has been closed.
// Passing these two checks also implies that surface_, page_surface_, and
@ -354,14 +490,15 @@ bool PdfPsMetafile::SaveTo(const FilePath& filename) const {
return true;
}
void PdfPsMetafile::CleanUp() {
void PdfPsMetafile::CleanUpAll() {
CleanUpContext(&context_);
CleanUpSurface(&surface_);
CleanUpContext(&page_context_);
CleanUpSurface(&page_surface_);
current_page_.clear();
all_pages_.clear();
CleanUpFonts();
CleanUpFreeType();
}
} // namespace printing

@ -64,6 +64,10 @@ class PdfPsMetafile {
// Closes resulting PDF/PS file. No further rendering is allowed.
void Close();
// Selects the font associated with |font_id| in |context|.
// Return true on success.
static bool SelectFontById(cairo_t* context, uint32_t font_id);
// Returns size of PDF/PS contents stored in buffer |all_pages_|.
// This function should ONLY be called after PDF/PS file is closed.
unsigned int GetDataSize() const;
@ -80,7 +84,7 @@ class PdfPsMetafile {
private:
// Cleans up all resources.
void CleanUp();
void CleanUpAll();
FileFormat format_;

@ -6,6 +6,7 @@
#include <cairo.h>
#include "printing/pdf_ps_metafile_linux.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace skia {
@ -258,20 +259,45 @@ void VectorPlatformDevice::drawPosText(const SkDraw& draw,
// Text color.
ApplyPaintColor(paint);
const uint16_t* glyphIDs = static_cast<const uint16_t*>(text);
const uint16_t* glyph_ids = static_cast<const uint16_t*>(text);
// Draw each glyph by its path.
for (size_t i = 0; i < len / sizeof(uint16_t); ++i) {
uint16_t glyphID = glyphIDs[i];
SkPath textPath;
paint.getTextPath(&glyphID,
sizeof(uint16_t),
pos[i * scalarsPerPos],
(scalarsPerPos == 1) ?
constY :
pos[i * scalarsPerPos + 1],
&textPath);
drawPath(draw, textPath, paint);
// The style is either kFill_Style or kStroke_Style.
if (paint.getStyle() & SkPaint::kStroke_Style) {
ApplyStrokeStyle(paint);
// Draw each glyph by its path.
for (size_t i = 0; i < len / sizeof(uint16_t); ++i) {
uint16_t glyph_id = glyph_ids[i];
SkPath textPath;
paint.getTextPath(&glyph_id,
sizeof(uint16_t),
pos[i * scalarsPerPos],
(scalarsPerPos == 1) ?
constY :
pos[i * scalarsPerPos + 1],
&textPath);
drawPath(draw, textPath, paint);
}
} else { // kFill_Style.
// Selects correct font.
if (!printing::PdfPsMetafile::SelectFontById(
context_, paint.getTypeface()->uniqueID())) {
SkASSERT(false);
return;
}
cairo_set_font_size(context_, paint.getTextSize());
// Draw glyphs.
for (size_t i = 0; i < len / sizeof(uint16_t); ++i) {
uint16_t glyph_id = glyph_ids[i];
cairo_glyph_t glyph;
glyph.index = glyph_id;
glyph.x = pos[i * scalarsPerPos];
glyph.y = (scalarsPerPos == 1) ? constY : pos[i * scalarsPerPos + 1];
cairo_show_glyphs(context_, &glyph, 1);
}
}
}
@ -440,9 +466,9 @@ void VectorPlatformDevice::InternalDrawBitmap(const SkBitmap& bitmap,
SkAutoLockPixels image_lock(bitmap);
cairo_surface_t* bitmap_surface =
cairo_image_surface_create_for_data(
reinterpret_cast<unsigned char*>(bitmap.getPixels()),
CAIRO_FORMAT_ARGB32, src_size_x, src_size_y, bitmap.rowBytes());
cairo_image_surface_create_for_data(
reinterpret_cast<unsigned char*>(bitmap.getPixels()),
CAIRO_FORMAT_ARGB32, src_size_x, src_size_y, bitmap.rowBytes());
cairo_set_source_surface(context_, bitmap_surface, x, y);
cairo_paint_with_alpha(context_, static_cast<double>(alpha) / 255.);