Newer
Older
Import / applications / MakePDF / DocStyle.cpp
#include <map>
#include "DocStyle.h"
#include "Util.h"
#include "IniFileReader.h"


GENERATE_ENUM_SERIALIZATION_FUNCTIONS
(
 TextRole,
 Last,
 NormalRole,
 "Normal",
 "Strong",
 "Emphasis",
 "Strong Emphasis",
 "Paragraph",
 "Heading 5",
 "Heading 4",
 "Heading 3",
 "Heading 2",
 "Heading 1",
 "List Item - Level 1",
 "List Item - Level 2",
 "List Item - Level 3",
 "List Item - Level 4",
 "Numbered List Item - Level 1",
 "Numbered List Item - Level 2",
 "Numbered List Item - Level 3",
 "Numbered List Item - Level 4",
 "Strike Through",
 "Underline",
 "Quote",
 "Block Quote",
 "Code",
 "Link"
)


GENERATE_ENUM_SERIALIZATION_FUNCTIONS
(
 TextAlignment,
 TA_Last,
 TA_Left,
 "Left",
 "Center",
 "Right",
 "Justified"
)


GENERATE_ENUM_SERIALIZATION_FUNCTIONS
(
 BulletType,
 BT_Last,
 BT_None,
 "None",
 "Circle",
 "Dot",
 "Dash",
 "Point",
 "Number",
 "NumberDot",
 "NumberBracket",
 "NumberSlash"
)


GENERATE_ENUM_SERIALIZATION_FUNCTIONS
(
 FontStyle,
 FS_Last,
 FS_Normal,
 "Normal",
 "Italics",
 "Bold",
 "BoldItalics",
 "StrikeThrough",
 "Underline",
 "Light",
 "LightItalics",
 "Medium",
 "MediumItalics",
 "FixedWidth"
)


std::string valueFromMap(std::map<std::string,std::string> a_map, std::string key, std::string a_default)
{
  return (a_map.count(key)) ? a_map.at(key) : a_default;
}


TextRoleProperties TextRolePropertiesFromIniSection(const IniSection& iniSection)
{
  std::map<std::string,std::string> nameValuePairs = IniSectionAsMap(iniSection);
  TextRoleProperties properties;
  properties.m_fontName             = stringToString(       valueFromMap(nameValuePairs, "FontFamily",      "Arial")  );
  properties.m_fontStyle            = stringToFontStyle(    valueFromMap(nameValuePairs, "FontStyle",       "Normal") );
  properties.m_fontSize             = stringToFloat(        valueFromMap(nameValuePairs, "FontSize",        "12.0")   );
  properties.m_foregroundColorIndex = stringToInt(          valueFromMap(nameValuePairs, "ForegroundColor", "0")      );
  properties.m_backgroundColorIndex = stringToInt(          valueFromMap(nameValuePairs, "BackgroundColor", "1")      );
  properties.m_textIndent           = stringToFloat(        valueFromMap(nameValuePairs, "TextIndent",      "0.0")    );
  properties.m_textAlignment        = stringToTextAlignment(valueFromMap(nameValuePairs, "TextAlignment",   "Left")   );
  properties.m_bulletIndent         = stringToFloat(        valueFromMap(nameValuePairs, "BilletIndent",    "0.0")    );
  properties.m_bulletType           = stringToBulletType(   valueFromMap(nameValuePairs, "BulletType",      "None")   );
  properties.m_lineSpacing          = stringToFloat(        valueFromMap(nameValuePairs, "LineSpacing",     "1.0")    );
  properties.m_spacing              = stringToFloat(        valueFromMap(nameValuePairs, "Spacing",         "0.0")    );
  return properties;
}


IniSection TextRolePropertiesToIniSection(const TextRoleProperties& properties)
{
  IniSection section;
  IniSectionAddValue(section, "FontFamily",      stringFromString(       properties.m_fontName             ));
  IniSectionAddValue(section, "FontStyle",       stringFromFontStyle(    properties.m_fontStyle            ));
  IniSectionAddValue(section, "FontSize",        stringFromFloat(        properties.m_fontSize             ));
  IniSectionAddValue(section, "ForegroundColor", stringFromInt(          properties.m_foregroundColorIndex ));
  IniSectionAddValue(section, "BackgroundColor", stringFromInt(          properties.m_backgroundColorIndex ));
  IniSectionAddValue(section, "TextIndent",      stringFromFloat(        properties.m_textIndent           ));
  IniSectionAddValue(section, "TextAlignment",   stringFromTextAlignment(properties.m_textAlignment        ));
  IniSectionAddValue(section, "BilletIndent",    stringFromFloat(        properties.m_bulletIndent         ));
  IniSectionAddValue(section, "BulletType",      stringFromBulletType(   properties.m_bulletType           ));
  IniSectionAddValue(section, "LineSpacing",     stringFromFloat(        properties.m_lineSpacing          ));
  IniSectionAddValue(section, "Spacing",         stringFromFloat(        properties.m_spacing              ));
  return section;
}


TextRoleProperties DefaultTextRoleProperties()
{
  TextRoleProperties defaultProps;
  defaultProps.m_fontName             = "Arial";
  defaultProps.m_fontStyle            = FS_Normal;
  defaultProps.m_fontSize             = 12.0;
  defaultProps.m_foregroundColorIndex = 0;
  defaultProps.m_backgroundColorIndex = 1;
  defaultProps.m_textIndent           = 0.0;
  defaultProps.m_textAlignment        = TA_Left;
  defaultProps.m_bulletIndent         = 0.0;
  defaultProps.m_bulletType           = BT_None;
  defaultProps.m_lineSpacing          = 1.0;
  defaultProps.m_spacing              = 0.0;
  return defaultProps;
}


bool ReadRole(std::map<TextRole, TextRoleProperties>& m_rolePropertiesMap,
          const std::map<std::string,IniSection>& iniMap,
          TextRole role,
          std::string roleStr,
          const TextRoleProperties& defaults)
{
  bool roleInMap = iniMap.count(roleStr);
  m_rolePropertiesMap[role] = (roleInMap) ? TextRolePropertiesFromIniSection(iniMap.at(roleStr)) : defaults;
  m_rolePropertiesMap[role].m_displayName = stringFromTextRole(role);
  return roleInMap;
}


bool WriteRole(const std::map<TextRole, TextRoleProperties>& m_rolePropertiesMap,
          std::map<std::string,IniSection>& iniMap,
          TextRole role,
          std::string roleStr,
          const TextRoleProperties& defaults)
{
  bool roleInMap = m_rolePropertiesMap.count(role);
  iniMap[roleStr] = TextRolePropertiesToIniSection((roleInMap) ? m_rolePropertiesMap.at(role) : defaults);
  return roleInMap;
}


struct DocStyle::Pimpl
{
  std::string m_name;
 
  // Palette info
  std::map<std::string, uint32_t> m_paletteEntries;
  std::string m_selectedPalette;

  // Text info
  TextRoleProperties m_defaults;
  std::map<TextRole, TextRoleProperties>  m_rolePropertiesMap;
  //fontStyles[currentStyle], fontSizes[currentHeadingLevel[stackDepth]]
  //HPDF_Font* fontStyles[4];
};


DocStyle::DocStyle() : m_pimpl(std::make_unique<Pimpl>())
{
}


DocStyle::~DocStyle()
{
}


std::string DocStyle::name()
{
  return m_pimpl->m_name;
}


void DocStyle::setName(const std::string& name)
{
  m_pimpl->m_name = name;
}


void DocStyle::readStyleFile(const char* a_fileName)
{
  IniFile iniFile = IniFileReader(a_fileName);
  std::map<std::string,IniSection> iniFileMap = IniFileAsMap(iniFile);
  TextRoleProperties defaults = DefaultTextRoleProperties();

  std::map<std::string,std::string> styleSectionMap = IniSectionAsMap(iniFileMap["Style"]);
  m_pimpl->m_name            = valueFromMap(styleSectionMap, "Name",    "Untitled");
  m_pimpl->m_selectedPalette = valueFromMap(styleSectionMap, "Palette", "");

  IniSection palettesSection = iniFileMap["Palettes"];
  for (auto val : palettesSection.values)
  {
    int col = stringToInt(val.value);
    m_pimpl->m_paletteEntries.emplace(val.name, *((uint32_t*)&col));
  }

  // TODO: switch to using the enum serialization stuff to iterate the roles
  if (ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NormalRole            , "Normal",                 defaults))
    defaults = m_pimpl->m_rolePropertiesMap[NormalRole];  // Make normal the defaults if it was specified
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrongRole                , "Strong",                 defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, EmphasisRole              , "Emphasis",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrongEmphasisRole        , "StrongEmphasis" ,        defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ParagraphRole             , "Paragraph",              defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading5Role              , "Heading5",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading4Role              , "Heading4",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading3Role              , "Heading3",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading2Role              , "Heading2",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading1Role              , "Heading1",               defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel1Role        , "ListItemLevel1",         defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel2Role        , "ListItemLevel2",         defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel3Role        , "ListItemLevel3",         defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel4Role        , "ListItemLevel4",         defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel1Role, "NumberedListItemLevel1", defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel2Role, "NumberedListItemLevel2", defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel3Role, "NumberedListItemLevel3", defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel4Role, "NumberedListItemLevel4", defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrikethroughRole         , "Strikethrough" ,         defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, UnderlineRole             , "Underline" ,             defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, QuoteRole                 , "Quote",                  defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, BlockQuoteRole            , "BlockQuote",             defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, CodeRole                  , "Code",                   defaults);
  ReadRole(m_pimpl->m_rolePropertiesMap, iniFileMap, LinkRole                  , "Link",                   defaults);
}


void DocStyle::writeStyleFile(const char* a_fileName)
{
  TextRoleProperties defaults = DefaultTextRoleProperties();
  std::map<std::string,IniSection> iniFileMap;

  IniSection styleSection;
  styleSection.name = "Style";
  IniSectionAddValue(styleSection, "Name",    m_pimpl->m_name);
  IniSectionAddValue(styleSection, "Palette", m_pimpl->m_selectedPalette);
  iniFileMap.emplace("Style", styleSection);

  IniSection palettesSection;
  palettesSection.name = "Palettes";
  for (auto val : m_pimpl->m_paletteEntries)
  {
    IniSectionAddValue(palettesSection, val.first, stringFromInt(*((int*)&val.second)));
  }
  iniFileMap.emplace("Palettes", palettesSection);

  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NormalRole                , "Normal",                 defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrongRole                , "Strong",                 defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, EmphasisRole              , "Emphasis",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrongEmphasisRole        , "StrongEmphasis" ,        defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ParagraphRole             , "Paragraph",              defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading5Role              , "Heading5",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading4Role              , "Heading4",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading3Role              , "Heading3",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading2Role              , "Heading2",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, Heading1Role              , "Heading1",               defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel1Role        , "ListItemLevel1",         defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel2Role        , "ListItemLevel2",         defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel3Role        , "ListItemLevel3",         defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, ListItemLevel4Role        , "ListItemLevel4",         defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel1Role, "NumberedListItemLevel1", defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel2Role, "NumberedListItemLevel2", defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel3Role, "NumberedListItemLevel3", defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, NumberedListItemLevel4Role, "NumberedListItemLevel4", defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, StrikethroughRole         , "Strikethrough" ,         defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, UnderlineRole             , "Underline" ,             defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, QuoteRole                 , "Quote",                  defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, BlockQuoteRole            , "BlockQuote",             defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, CodeRole                  , "Code",                   defaults);
  WriteRole(m_pimpl->m_rolePropertiesMap, iniFileMap, LinkRole                  , "Link",                   defaults);

  if (!IniFileWriter(a_fileName, IniFileFromMap(iniFileMap)))
  {
    printf("error while trying to save style to %s\n", a_fileName);
  }
}


std::vector<std::string> DocStyle::paletteList()
{
  return map2keys(m_pimpl->m_paletteEntries);
}


std::string DocStyle::selectedPalette()
{
  return m_pimpl->m_selectedPalette;
}


uint32_t DocStyle::palette(const std::string& a_palette)
{
  return m_pimpl->m_paletteEntries[a_palette];
}


void DocStyle::selectPalette(const std::string& a_palette)
{
  m_pimpl->m_selectedPalette = a_palette;
}


void DocStyle::addPalette(const std::string& a_palette, uint32_t value)
{
  m_pimpl->m_paletteEntries.emplace(a_palette, value);
}


void DocStyle::editPalette(const std::string& a_palette, uint32_t value)
{
  m_pimpl->m_paletteEntries[a_palette] = value;
}


void DocStyle::removePalette(const std::string& a_palette)
{
  m_pimpl->m_paletteEntries.erase(a_palette);
  if (m_pimpl->m_selectedPalette == a_palette)
  {
    m_pimpl->m_selectedPalette = (m_pimpl->m_paletteEntries.size()) ? m_pimpl->m_paletteEntries.begin()->first : "";
  }
}


TextRoleProperties DocStyle::textProperties(TextRole a_role)
{
  bool inMap = m_pimpl->m_rolePropertiesMap.count(a_role);
  TextRoleProperties props = (inMap) ? m_pimpl->m_rolePropertiesMap.at(a_role) : DefaultTextRoleProperties();
  props.m_displayName = stringFromTextRole(a_role);
  return props;
}


void DocStyle::setTextProperties(TextRole a_role, TextRoleProperties a_properties)
{
  m_pimpl->m_rolePropertiesMap[a_role] = a_properties;
}


void DocStyle::font(TextRole a_role, std::string& a_fontName, float& a_fontSize)
{
  TextRoleProperties props = m_pimpl->m_defaults;
  if (m_pimpl->m_rolePropertiesMap.find(a_role) != m_pimpl->m_rolePropertiesMap.end())
    props = m_pimpl->m_rolePropertiesMap[a_role];
  a_fontName = props.m_fontName;
  a_fontSize = props.m_fontSize;
}


void DocStyle::foregroundColor(TextRole a_role, float& a_red, float& a_green, float& a_blue, float& a_alpha)
{
  TextRoleProperties props = m_pimpl->m_defaults;
  if (m_pimpl->m_rolePropertiesMap.find(a_role) != m_pimpl->m_rolePropertiesMap.end())
    props = m_pimpl->m_rolePropertiesMap[a_role];
  a_red   = props.m_foregoundColor[0];
  a_green = props.m_foregoundColor[1];
  a_blue  = props.m_foregoundColor[2];
  a_alpha = props.m_foregoundColor[3];
}


void DocStyle::backgroundColor(TextRole a_role, float& a_red, float& a_green, float& a_blue, float& a_alpha)
{
  TextRoleProperties props = m_pimpl->m_defaults;
  if (m_pimpl->m_rolePropertiesMap.find(a_role) != m_pimpl->m_rolePropertiesMap.end())
    props = m_pimpl->m_rolePropertiesMap[a_role];
  a_red   = props.m_backgoundColor[0];
  a_green = props.m_backgoundColor[1];
  a_blue  = props.m_backgoundColor[2];
  a_alpha = props.m_backgoundColor[3];
}


void DocStyle::adornNewPage(DocOutputPage& page)
{
  // Any background things can be drawn here
}


void DocStyle::adornOldPage(DocOutputPage& page)
{
  page.setFontType(FT_Normal);
  page.setFontSize(12.0f);
  const char* text = "PDF created using WickedDocs";
  float width = page.textWidth(text);

  page.setAlpha(0.5);
  page.setFillColor(0x0);
  page.drawText((page.width() - width) * 0.5f - 0.8, page.height() - 12.0f + 0.8, text);
  page.setFillColor(0xffffff);
  page.drawText((page.width() - width) * 0.5f + 0.8, page.height() - 12.0f - 0.8, text);
  page.setAlpha(1.0);
  page.setFillColor(0x338496);
  page.drawText((page.width() - width) * 0.5f, page.height() - 12.0f, text);
}