1/* 2 * Copyright 2008-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Fran��ois Revol <revol@free.fr> 7 */ 8 9#include "StyledTextImporter.h" 10 11#include <new> 12#include <stdint.h> 13#include <stdio.h> 14 15#include <Alert.h> 16#include <Archivable.h> 17#include <ByteOrder.h> 18#include <Catalog.h> 19#include <DataIO.h> 20#include <File.h> 21#include <List.h> 22#include <Locale.h> 23#include <Message.h> 24#include <NodeInfo.h> 25#include <Shape.h> 26#include <String.h> 27#include <TextView.h> 28 29#include "Defines.h" 30#include "Icon.h" 31#include "PathContainer.h" 32#include "Shape.h" 33#include "Style.h" 34#include "StyleContainer.h" 35#include "VectorPath.h" 36 37 38#undef B_TRANSLATION_CONTEXT 39#define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyledTextImport" 40//#define CALLED() printf("%s()\n", __FUNCTION__); 41#define CALLED() do {} while (0) 42 43 44using std::nothrow; 45 46class ShapeIterator : public BShapeIterator { 47 public: 48 ShapeIterator(Icon *icon, Shape *to, BPoint offset, const char *name); 49 ~ShapeIterator() {}; 50 51 virtual status_t IterateMoveTo(BPoint *point); 52 virtual status_t IterateLineTo(int32 lineCount, BPoint *linePts); 53 virtual status_t IterateBezierTo(int32 bezierCount, BPoint *bezierPts); 54 virtual status_t IterateClose(); 55 56 private: 57 VectorPath *CurrentPath(); 58 void NextPath(); 59 60 Icon *fIcon; 61 Shape *fShape; 62 VectorPath *fPath; 63 BPoint fOffset; 64 const char *fName; 65 BPoint fLastPoint; 66 bool fHasLastPoint; 67}; 68 69ShapeIterator::ShapeIterator(Icon *icon, Shape *to, BPoint offset, 70 const char *name) 71{ 72 CALLED(); 73 fIcon = icon; 74 fShape = to; 75 fPath = NULL; 76 fOffset = offset; 77 fName = name; 78 fLastPoint = offset; 79 fHasLastPoint = false; 80} 81 82status_t 83ShapeIterator::IterateMoveTo(BPoint *point) 84{ 85 CALLED(); 86 if (fPath) 87 NextPath(); 88 if (!CurrentPath()) 89 return B_ERROR; 90 //fPath->AddPoint(fOffset + *point); 91 fLastPoint = fOffset + *point; 92 fHasLastPoint = true; 93 return B_OK; 94} 95 96status_t 97ShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePts) 98{ 99 CALLED(); 100 if (!CurrentPath()) 101 return B_ERROR; 102 while (lineCount--) { 103 fPath->AddPoint(fOffset + *linePts); 104 fLastPoint = fOffset + *linePts; 105 fHasLastPoint = true; 106 linePts++; 107 } 108 return B_OK; 109} 110 111status_t 112ShapeIterator::IterateBezierTo(int32 bezierCount, BPoint *bezierPts) 113{ 114 CALLED(); 115 if (!CurrentPath()) 116 return B_ERROR; 117 BPoint start(bezierPts[0]); 118 if (fHasLastPoint) 119 start = fLastPoint; 120 while (bezierCount--) { 121 fPath->AddPoint(fOffset + bezierPts[0], 122 fLastPoint, fOffset + bezierPts[1], true); 123 fLastPoint = fOffset + bezierPts[2]; 124 bezierPts += 3; 125 } 126 fPath->AddPoint(fLastPoint); 127 fHasLastPoint = true; 128 return B_OK; 129} 130 131status_t 132ShapeIterator::IterateClose() 133{ 134 CALLED(); 135 if (!CurrentPath()) 136 return B_ERROR; 137 fPath->SetClosed(true); 138 NextPath(); 139 fHasLastPoint = false; 140 return B_OK; 141} 142 143VectorPath * 144ShapeIterator::CurrentPath() 145{ 146 CALLED(); 147 if (fPath) 148 return fPath; 149 fPath = new (nothrow) VectorPath(); 150 fPath->SetName(fName); 151 return fPath; 152} 153 154void 155ShapeIterator::NextPath() 156{ 157 CALLED(); 158 if (fPath) { 159 fIcon->Paths()->AddPath(fPath); 160 fShape->Paths()->AddPath(fPath); 161 } 162 fPath = NULL; 163} 164 165 166// #pragma mark - 167 168// constructor 169StyledTextImporter::StyledTextImporter() 170 : Importer(), 171 fStyleMap(NULL), 172 fStyleCount(0) 173{ 174 CALLED(); 175} 176 177// destructor 178StyledTextImporter::~StyledTextImporter() 179{ 180 CALLED(); 181} 182 183// Import 184status_t 185StyledTextImporter::Import(Icon* icon, BMessage* clipping) 186{ 187 CALLED(); 188 const char *text; 189 int32 textLength; 190 191 if (clipping == NULL) 192 return ENOENT; 193 if (clipping->FindData("text/plain", 194 B_MIME_TYPE, (const void **)&text, &textLength) == B_OK) { 195 text_run_array *runs = NULL; 196 int32 runsLength; 197 if (clipping->FindData("application/x-vnd.Be-text_run_array", 198 B_MIME_TYPE, (const void **)&runs, &runsLength) < B_OK) 199 runs = NULL; 200 BString str(text, textLength); 201 return _Import(icon, str.String(), runs); 202 } 203 return ENOENT; 204} 205 206// Import 207status_t 208StyledTextImporter::Import(Icon* icon, const entry_ref* ref) 209{ 210 CALLED(); 211 status_t err; 212 BFile file(ref, B_READ_ONLY); 213 err = file.InitCheck(); 214 if (err < B_OK) 215 return err; 216 217 BNodeInfo info(&file); 218 char mime[B_MIME_TYPE_LENGTH]; 219 err = info.GetType(mime); 220 if (err < B_OK) 221 return err; 222 223 if (strncmp(mime, "text/plain", B_MIME_TYPE_LENGTH)) 224 return EINVAL; 225 226 off_t size; 227 err = file.GetSize(&size); 228 if (err < B_OK) 229 return err; 230 if (size > 1 * 1024 * 1024) // Don't load files that big 231 return E2BIG; 232 233 BMallocIO mio; 234 mio.SetSize((size_t)size + 1); 235 memset((void *)mio.Buffer(), 0, (size_t)size + 1); 236 237 // TODO: read runs from attribute 238 239 return _Import(icon, (const char *)mio.Buffer(), NULL); 240} 241 242// _Import 243status_t 244StyledTextImporter::_Import(Icon* icon, const char *text, text_run_array *runs) 245{ 246 CALLED(); 247 status_t ret = Init(icon); 248 if (ret < B_OK) { 249 printf("StyledTextImporter::Import() - " 250 "Init() error: %s\n", strerror(ret)); 251 return ret; 252 } 253 254 BString str(text); 255 if (str.Length() > 50) { 256 BAlert* alert = new BAlert(B_TRANSLATE("too big"), 257 B_TRANSLATE("The text you are trying to import is quite long," 258 "are you sure?"), 259 B_TRANSLATE("Yes"), B_TRANSLATE("No"), NULL, 260 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 261 if (alert->Go()) 262 return B_CANCELED; 263 } 264 265 // import run colors as styles 266 if (runs) { 267 delete[] fStyleMap; 268 fStyleMap = new struct style_map[runs->count]; 269 for (int32 i = 0; runs && i < runs->count; i++) { 270 _AddStyle(icon, &runs->runs[i]); 271 } 272 } 273 274 int32 currentRun = 0; 275 text_run *run = NULL; 276 if (runs) 277 run = &runs->runs[0]; 278 int32 len = str.Length(); 279 int32 chars = str.CountChars(); 280 BPoint origin(0,0); 281 BPoint offset(origin); 282 283 for (int32 i = 0, c = 0; i < len && c < chars; c++) { 284 // make sure we are still on the (good) run 285 while (run && currentRun < runs->count - 1 && 286 i >= runs->runs[currentRun + 1].offset) { 287 run = &runs->runs[++currentRun]; 288 //printf("switching to run %d\n", currentRun); 289 } 290 291 int charLen; 292 for (charLen = 1; str.ByteAt(i + charLen) & 0x80; charLen++); 293 294 BShape glyph; 295 BShape *glyphs[1] = { &glyph }; 296 BFont font(be_plain_font); 297 if (run) 298 font = run->font; 299 300 // first char 301 if (offset == BPoint(0,0)) { 302 font_height height; 303 font.GetHeight(&height); 304 origin.y += height.ascent; 305 offset = origin; 306 } 307 // LF 308 if (str[i] == '\n') { 309 // XXX: should take the MAX() for the line 310 // XXX: should use descent + leading from previous line 311 font_height height; 312 font.GetHeight(&height); 313 origin.y += height.ascent + height.descent + height.leading; 314 offset = origin; 315 i++; 316 continue; 317 } 318 319 float charWidth; 320 charWidth = font.StringWidth(str.String() + i, charLen); 321 //printf("StringWidth( %d) = %f\n", charLen, charWidth); 322 BString glyphName(str.String() + i, charLen); 323 glyphName.Prepend("Glyph ("); 324 glyphName.Append(")"); 325 326 font.GetGlyphShapes((str.String() + i), 1, glyphs); 327 if (glyph.Bounds().IsValid()) { 328 //offset.x += glyph.Bounds().Width(); 329 offset.x += charWidth; 330 Shape* shape = new (nothrow) Shape(NULL); 331 if (shape == NULL) 332 return B_NO_MEMORY; 333 shape->SetName(glyphName.String()); 334 if (!icon->Shapes()->AddShape(shape)) { 335 delete shape; 336 return B_NO_MEMORY; 337 } 338 for (int j = 0; run && j < fStyleCount; j++) { 339 if (fStyleMap[j].run == run) { 340 shape->SetStyle(fStyleMap[j].style); 341 break; 342 } 343 } 344 ShapeIterator iterator(icon, shape, offset, glyphName.String()); 345 if (iterator.Iterate(&glyph) < B_OK) 346 return B_ERROR; 347 348 } 349 350 // skip the rest of UTF-8 char bytes 351 for (i++; i < len && str[i] & 0x80; i++); 352 } 353 354 delete[] fStyleMap; 355 fStyleMap = NULL; 356 357 return B_OK; 358} 359 360// #pragma mark - 361 362// _AddStyle 363status_t 364StyledTextImporter::_AddStyle(Icon *icon, text_run *run) 365{ 366 CALLED(); 367 if (!run) 368 return EINVAL; 369 rgb_color color = run->color; 370 Style* style = new(std::nothrow) Style(color); 371 if (style == NULL) 372 return B_NO_MEMORY; 373 char name[30]; 374 sprintf(name, B_TRANSLATE("Color (#%02x%02x%02x)"), 375 color.red, color.green, color.blue); 376 style->SetName(name); 377 378 bool found = false; 379 for (int i = 0; i < fStyleCount; i++) { 380 if (*style == *(fStyleMap[i].style)) { 381 delete style; 382 style = fStyleMap[i].style; 383 found = true; 384 break; 385 } 386 } 387 388 if (!found && !icon->Styles()->AddStyle(style)) { 389 delete style; 390 return B_NO_MEMORY; 391 } 392 393 fStyleMap[fStyleCount].run = run; 394 fStyleMap[fStyleCount].style = style; 395 fStyleCount++; 396 397 return B_OK; 398} 399 400// _AddPaths 401status_t 402StyledTextImporter::_AddPaths(Icon *icon, BShape *shape) 403{ 404 CALLED(); 405 return B_ERROR; 406} 407 408// _AddShape 409status_t 410StyledTextImporter::_AddShape(Icon *icon, BShape *shape, text_run *run) 411{ 412 CALLED(); 413 return B_ERROR; 414} 415