1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci// This sample progam demonstrates how to use Skia and HarfBuzz to 9cb93a386Sopenharmony_ci// produce a PDF file from UTF-8 text in stdin. 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include <cassert> 12cb93a386Sopenharmony_ci#include <cstdlib> 13cb93a386Sopenharmony_ci#include <iostream> 14cb93a386Sopenharmony_ci#include <map> 15cb93a386Sopenharmony_ci#include <sstream> 16cb93a386Sopenharmony_ci#include <string> 17cb93a386Sopenharmony_ci#include <vector> 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 20cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 21cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h" 22cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h" 23cb93a386Sopenharmony_ci#include "include/docs/SkPDFDocument.h" 24cb93a386Sopenharmony_ci#include "modules/skshaper/include/SkShaper.h" 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci// Options ///////////////////////////////////////////////////////////////////// 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_cistruct BaseOption { 29cb93a386Sopenharmony_ci std::string selector; 30cb93a386Sopenharmony_ci std::string description; 31cb93a386Sopenharmony_ci virtual void set(std::string _value) = 0; 32cb93a386Sopenharmony_ci virtual std::string valueToString() = 0; 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ci BaseOption(std::string _selector, std::string _description) 35cb93a386Sopenharmony_ci : selector(_selector), description(_description) {} 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ci // Required until C++17 copy elision 38cb93a386Sopenharmony_ci BaseOption(const BaseOption&) = default; 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_ci virtual ~BaseOption() {} 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci static void Init(const std::vector<BaseOption*> &, int argc, char **argv); 43cb93a386Sopenharmony_ci}; 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_citemplate <class T> 46cb93a386Sopenharmony_cistruct Option : BaseOption { 47cb93a386Sopenharmony_ci T value; 48cb93a386Sopenharmony_ci Option(std::string _selector, std::string _description, T defaultValue) 49cb93a386Sopenharmony_ci : BaseOption(_selector, _description), value(defaultValue) {} 50cb93a386Sopenharmony_ci}; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_civoid BaseOption::Init(const std::vector<BaseOption*> &option_list, 53cb93a386Sopenharmony_ci int argc, char **argv) { 54cb93a386Sopenharmony_ci std::map<std::string, BaseOption *> options; 55cb93a386Sopenharmony_ci for (BaseOption *opt : option_list) { 56cb93a386Sopenharmony_ci options[opt->selector] = opt; 57cb93a386Sopenharmony_ci } 58cb93a386Sopenharmony_ci for (int i = 1; i < argc; i++) { 59cb93a386Sopenharmony_ci std::string option_selector(argv[i]); 60cb93a386Sopenharmony_ci auto it = options.find(option_selector); 61cb93a386Sopenharmony_ci if (it != options.end()) { 62cb93a386Sopenharmony_ci if (i >= argc) { 63cb93a386Sopenharmony_ci break; 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci const char *option_value = argv[i + 1]; 66cb93a386Sopenharmony_ci it->second->set(option_value); 67cb93a386Sopenharmony_ci i++; 68cb93a386Sopenharmony_ci } else { 69cb93a386Sopenharmony_ci printf("Ignoring unrecognized option: %s.\n", argv[i]); 70cb93a386Sopenharmony_ci printf("Usage: %s {option value}\n", argv[0]); 71cb93a386Sopenharmony_ci printf("\tTakes text from stdin and produces pdf file.\n"); 72cb93a386Sopenharmony_ci printf("Supported options:\n"); 73cb93a386Sopenharmony_ci for (BaseOption *opt : option_list) { 74cb93a386Sopenharmony_ci printf("\t%s\t%s (%s)\n", opt->selector.c_str(), 75cb93a386Sopenharmony_ci opt->description.c_str(), opt->valueToString().c_str()); 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci exit(-1); 78cb93a386Sopenharmony_ci } 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci} 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_cistruct DoubleOption : Option<double> { 83cb93a386Sopenharmony_ci void set(std::string _value) override { value = atof(_value.c_str()); } 84cb93a386Sopenharmony_ci std::string valueToString() override { 85cb93a386Sopenharmony_ci std::ostringstream stm; 86cb93a386Sopenharmony_ci stm << value; 87cb93a386Sopenharmony_ci return stm.str(); 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci DoubleOption(std::string _selector, 90cb93a386Sopenharmony_ci std::string _description, 91cb93a386Sopenharmony_ci double defaultValue) 92cb93a386Sopenharmony_ci : Option<double>(_selector, _description, defaultValue) {} 93cb93a386Sopenharmony_ci}; 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_cistruct StringOption : Option<std::string> { 96cb93a386Sopenharmony_ci void set(std::string _value) override { value = _value; } 97cb93a386Sopenharmony_ci std::string valueToString() override { return value; } 98cb93a386Sopenharmony_ci StringOption(std::string _selector, 99cb93a386Sopenharmony_ci std::string _description, 100cb93a386Sopenharmony_ci std::string defaultValue) 101cb93a386Sopenharmony_ci : Option<std::string>(_selector, _description, defaultValue) {} 102cb93a386Sopenharmony_ci}; 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci// Config ////////////////////////////////////////////////////////////////////// 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_cistruct Config { 107cb93a386Sopenharmony_ci DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f); 108cb93a386Sopenharmony_ci DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f); 109cb93a386Sopenharmony_ci StringOption title = StringOption("-t", "PDF title", "---"); 110cb93a386Sopenharmony_ci StringOption author = StringOption("-a", "PDF author", "---"); 111cb93a386Sopenharmony_ci StringOption subject = StringOption("-k", "PDF subject", "---"); 112cb93a386Sopenharmony_ci StringOption keywords = StringOption("-c", "PDF keywords", "---"); 113cb93a386Sopenharmony_ci StringOption creator = StringOption("-t", "PDF creator", "---"); 114cb93a386Sopenharmony_ci StringOption font_file = StringOption("-f", ".ttf font file", ""); 115cb93a386Sopenharmony_ci DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f); 116cb93a386Sopenharmony_ci DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f); 117cb93a386Sopenharmony_ci DoubleOption line_spacing_ratio = 118cb93a386Sopenharmony_ci DoubleOption("-h", "Line spacing ratio", 0.25f); 119cb93a386Sopenharmony_ci StringOption output_file_name = 120cb93a386Sopenharmony_ci StringOption("-o", ".pdf output file name", "out-skiahf.pdf"); 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_ci Config(int argc, char **argv) { 123cb93a386Sopenharmony_ci BaseOption::Init(std::vector<BaseOption*>{ 124cb93a386Sopenharmony_ci &page_width, &page_height, &title, &author, &subject, 125cb93a386Sopenharmony_ci &keywords, &creator, &font_file, &font_size, &left_margin, 126cb93a386Sopenharmony_ci &line_spacing_ratio, &output_file_name}, argc, argv); 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci}; 129cb93a386Sopenharmony_ci 130cb93a386Sopenharmony_ci// Placement /////////////////////////////////////////////////////////////////// 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ciclass Placement { 133cb93a386Sopenharmony_cipublic: 134cb93a386Sopenharmony_ci Placement(const Config* conf, SkDocument *doc) 135cb93a386Sopenharmony_ci : config(conf), document(doc), pageCanvas(nullptr) { 136cb93a386Sopenharmony_ci white_paint.setColor(SK_ColorWHITE); 137cb93a386Sopenharmony_ci glyph_paint.setColor(SK_ColorBLACK); 138cb93a386Sopenharmony_ci glyph_paint.setAntiAlias(true); 139cb93a386Sopenharmony_ci font.setEdging(SkFont::Edging::kSubpixelAntiAlias); 140cb93a386Sopenharmony_ci font.setSubpixel(true); 141cb93a386Sopenharmony_ci font.setSize(SkDoubleToScalar(config->font_size.value)); 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) { 145cb93a386Sopenharmony_ci SkTextBlobBuilderRunHandler textBlobBuilder(text, {0, 0}); 146cb93a386Sopenharmony_ci shaper.shape(text, textBytes, font, true, 147cb93a386Sopenharmony_ci config->page_width.value - 2*config->left_margin.value, &textBlobBuilder); 148cb93a386Sopenharmony_ci SkPoint endPoint = textBlobBuilder.endPoint(); 149cb93a386Sopenharmony_ci sk_sp<const SkTextBlob> blob = textBlobBuilder.makeBlob(); 150cb93a386Sopenharmony_ci // If we don't have a page, or if we're not at the start of the page and the blob won't fit 151cb93a386Sopenharmony_ci if (!pageCanvas || 152cb93a386Sopenharmony_ci (current_y > config->line_spacing_ratio.value * config->font_size.value && 153cb93a386Sopenharmony_ci current_y + endPoint.y() > config->page_height.value) 154cb93a386Sopenharmony_ci ) { 155cb93a386Sopenharmony_ci if (pageCanvas) { 156cb93a386Sopenharmony_ci document->endPage(); 157cb93a386Sopenharmony_ci } 158cb93a386Sopenharmony_ci pageCanvas = document->beginPage( 159cb93a386Sopenharmony_ci SkDoubleToScalar(config->page_width.value), 160cb93a386Sopenharmony_ci SkDoubleToScalar(config->page_height.value)); 161cb93a386Sopenharmony_ci pageCanvas->drawPaint(white_paint); 162cb93a386Sopenharmony_ci current_x = config->left_margin.value; 163cb93a386Sopenharmony_ci current_y = config->line_spacing_ratio.value * config->font_size.value; 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci pageCanvas->drawTextBlob( 166cb93a386Sopenharmony_ci blob.get(), SkDoubleToScalar(current_x), 167cb93a386Sopenharmony_ci SkDoubleToScalar(current_y), glyph_paint); 168cb93a386Sopenharmony_ci // Advance to the next line. 169cb93a386Sopenharmony_ci current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ciprivate: 173cb93a386Sopenharmony_ci const Config* config; 174cb93a386Sopenharmony_ci SkDocument *document; 175cb93a386Sopenharmony_ci SkCanvas *pageCanvas; 176cb93a386Sopenharmony_ci SkPaint white_paint; 177cb93a386Sopenharmony_ci SkPaint glyph_paint; 178cb93a386Sopenharmony_ci SkFont font; 179cb93a386Sopenharmony_ci double current_x; 180cb93a386Sopenharmony_ci double current_y; 181cb93a386Sopenharmony_ci}; 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////// 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_cistatic sk_sp<SkDocument> MakePDFDocument(const Config &config, SkWStream *wStream) { 186cb93a386Sopenharmony_ci SkPDF::Metadata pdf_info; 187cb93a386Sopenharmony_ci pdf_info.fTitle = config.title.value.c_str(); 188cb93a386Sopenharmony_ci pdf_info.fAuthor = config.author.value.c_str(); 189cb93a386Sopenharmony_ci pdf_info.fSubject = config.subject.value.c_str(); 190cb93a386Sopenharmony_ci pdf_info.fKeywords = config.keywords.value.c_str(); 191cb93a386Sopenharmony_ci pdf_info.fCreator = config.creator.value.c_str(); 192cb93a386Sopenharmony_ci #if 0 193cb93a386Sopenharmony_ci SkTime::DateTime now; 194cb93a386Sopenharmony_ci SkTime::GetDateTime(&now); 195cb93a386Sopenharmony_ci pdf_info.fCreation = now; 196cb93a386Sopenharmony_ci pdf_info.fModified = now; 197cb93a386Sopenharmony_ci pdf_info.fPDFA = true; 198cb93a386Sopenharmony_ci #endif 199cb93a386Sopenharmony_ci return SkPDF::MakeDocument(wStream, pdf_info); 200cb93a386Sopenharmony_ci} 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_ciint main(int argc, char **argv) { 203cb93a386Sopenharmony_ci Config config(argc, argv); 204cb93a386Sopenharmony_ci SkFILEWStream wStream(config.output_file_name.value.c_str()); 205cb93a386Sopenharmony_ci sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream); 206cb93a386Sopenharmony_ci assert(doc); 207cb93a386Sopenharmony_ci Placement placement(&config, doc.get()); 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ci const std::string &font_file = config.font_file.value; 210cb93a386Sopenharmony_ci sk_sp<SkTypeface> typeface; 211cb93a386Sopenharmony_ci if (font_file.size() > 0) { 212cb93a386Sopenharmony_ci typeface = SkTypeface::MakeFromFile(font_file.c_str(), 0 /* index */); 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci std::unique_ptr<SkShaper> shaper = SkShaper::Make(); 215cb93a386Sopenharmony_ci assert(shaper); 216cb93a386Sopenharmony_ci //SkString line("This is هذا هو الخط a line."); 217cb93a386Sopenharmony_ci //SkString line("This is a line هذا هو الخط."); 218cb93a386Sopenharmony_ci for (std::string line; std::getline(std::cin, line);) { 219cb93a386Sopenharmony_ci placement.WriteLine(*shaper, line.c_str(), line.size()); 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci doc->close(); 223cb93a386Sopenharmony_ci wStream.flush(); 224cb93a386Sopenharmony_ci return 0; 225cb93a386Sopenharmony_ci} 226