| Index: printing/pdf_ps_metafile_linux.cc
|
| ===================================================================
|
| --- printing/pdf_ps_metafile_linux.cc (revision 25867)
|
| +++ printing/pdf_ps_metafile_linux.cc (working copy)
|
| @@ -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 @@
|
| 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() {
|
| - // 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 @@
|
| // 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 @@
|
| 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 @@
|
| 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 @@
|
|
|
| default:
|
| NOTREACHED();
|
| - CleanUp();
|
| + CleanUpAll();
|
| return NULL;
|
| }
|
|
|
| @@ -175,15 +234,15 @@
|
| // 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 @@
|
| 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 @@
|
|
|
| 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 @@
|
| 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,8 +361,85 @@
|
|
|
| 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 {
|
| // 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.
|
| @@ -317,7 +453,7 @@
|
|
|
| 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,13 +490,15 @@
|
| 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
|
|
|