/* * Copyright 2000-2018 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.ui; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import com.vaadin.server.ComponentSizeValidator; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.AbstractComponentContainerState; /** * Extension to {@link AbstractComponent} that defines the default * implementation for the methods in {@link ComponentContainer}. Basic UI * components that need to contain other components inherit this class to easily * qualify as a component container. * * @author Vaadin Ltd * @since 3.0 */ @SuppressWarnings("serial") public abstract class AbstractComponentContainer extends AbstractComponent implements ComponentContainer { /** * Constructs a new component container. */ public AbstractComponentContainer() { super(); } /* * (non-Javadoc) * * @see * com.vaadin.ui.ComponentContainer#addComponents(com.vaadin.ui.Component[]) */ @Override public void addComponents(Component... components) { for (Component c : components) { addComponent(c); } } /** * Removes all components from the container. This should probably be * re-implemented in extending classes for a more powerful implementation. */ @Override public void removeAllComponents() { final LinkedList l = new LinkedList<>(); // Adds all components for (final Iterator i = getComponentIterator(); i .hasNext();) { l.add(i.next()); } // Removes all component for (Component aL : l) { removeComponent(aL); } } /* * Moves all components from an another container into this container. Don't * add a JavaDoc comment here, we use the default documentation from * implemented interface. */ @Override public void moveComponentsFrom(ComponentContainer source) { final LinkedList components = new LinkedList<>(); for (final Iterator i = source.getComponentIterator(); i .hasNext();) { components.add(i.next()); } for (final Component c : components) { source.removeComponent(c); addComponent(c); } } /* documented in interface */ @Override public Registration addComponentAttachListener( ComponentAttachListener listener) { return addListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentAttachListener( ComponentAttachListener listener) { removeListener(ComponentAttachEvent.class, listener, ComponentAttachListener.attachMethod); } /* documented in interface */ @Override public Registration addComponentDetachListener( ComponentDetachListener listener) { return addListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /* documented in interface */ @Override @Deprecated public void removeComponentDetachListener( ComponentDetachListener listener) { removeListener(ComponentDetachEvent.class, listener, ComponentDetachListener.detachMethod); } /** * Fires the component attached event. This should be called by the * addComponent methods after the component have been added to this * container. * * @param component * the component that has been added to this container. */ protected void fireComponentAttachEvent(Component component) { fireEvent(new ComponentAttachEvent(this, component)); } /** * Fires the component detached event. This should be called by the * removeComponent methods after the component have been removed from this * container. * * @param component * the component that has been removed from this container. */ protected void fireComponentDetachEvent(Component component) { fireEvent(new ComponentDetachEvent(this, component)); } /** * This only implements the events and component parent calls. The extending * classes must implement component list maintenance and call this method * after component list maintenance. * * @see com.vaadin.ui.ComponentContainer#addComponent(Component) */ @Override public void addComponent(Component c) { // Make sure we're not adding the component inside it's own content if (isOrHasAncestor(c)) { throw new IllegalArgumentException( "Component cannot be added inside it's own content"); } if (c.getParent() != null) { // If the component already has a parent, try to remove it AbstractSingleComponentContainer.removeFromParent(c); } c.setParent(this); fireComponentAttachEvent(c); markAsDirty(); } /** * This only implements the events and component parent calls. The extending * classes must implement component list maintenance and call this method * before component list maintenance. * * @see com.vaadin.ui.ComponentContainer#removeComponent(Component) */ @Override public void removeComponent(Component c) { if (equals(c.getParent())) { c.setParent(null); fireComponentDetachEvent(c); markAsDirty(); } } @Override public void setWidth(float width, Unit unit) { /* * child tree repaints may be needed, due to our fall back support for * invalid relative sizes */ Collection dirtyChildren = null; boolean childrenMayBecomeUndefined = false; if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) { // children currently in invalid state may need repaint dirtyChildren = getInvalidSizedChildren(false); } else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED) || (unit == Unit.PERCENTAGE && getWidthUnits() != Unit.PERCENTAGE && !ComponentSizeValidator .parentCanDefineWidth(this))) { /* * relative width children may get to invalid state if width becomes * invalid. Width may also become invalid if units become percentage * due to the fallback support */ childrenMayBecomeUndefined = true; dirtyChildren = getInvalidSizedChildren(false); } super.setWidth(width, unit); repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined, false); } private void repaintChangedChildTrees(Collection invalidChildren, boolean childrenMayBecomeUndefined, boolean vertical) { if (childrenMayBecomeUndefined) { Collection previouslyInvalidComponents = invalidChildren; invalidChildren = getInvalidSizedChildren(vertical); if (previouslyInvalidComponents != null && invalidChildren != null) { for (Iterator iterator = invalidChildren .iterator(); iterator.hasNext();) { Component component = iterator.next(); if (previouslyInvalidComponents.contains(component)) { // still invalid don't repaint iterator.remove(); } } } } else if (invalidChildren != null) { Collection stillInvalidChildren = getInvalidSizedChildren( vertical); if (stillInvalidChildren != null) { for (Component component : stillInvalidChildren) { // didn't become valid invalidChildren.remove(component); } } } if (invalidChildren != null) { repaintChildTrees(invalidChildren); } } private Collection getInvalidSizedChildren( final boolean vertical) { HashSet components = null; for (Component component : this) { boolean valid = vertical ? ComponentSizeValidator.checkHeights(component) : ComponentSizeValidator.checkWidths(component); if (!valid) { if (components == null) { components = new HashSet<>(); } components.add(component); } } return components; } private void repaintChildTrees(Collection dirtyChildren) { for (Component c : dirtyChildren) { c.markAsDirtyRecursive(); } } @Override public void setHeight(float height, Unit unit) { /* * child tree repaints may be needed, due to our fall back support for * invalid relative sizes */ Collection dirtyChildren = null; boolean childrenMayBecomeUndefined = false; if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) { // children currently in invalid state may need repaint dirtyChildren = getInvalidSizedChildren(true); } else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED) || (unit == Unit.PERCENTAGE && getHeightUnits() != Unit.PERCENTAGE && !ComponentSizeValidator .parentCanDefineHeight(this))) { /* * relative height children may get to invalid state if height * becomes invalid. Height may also become invalid if units become * percentage due to the fallback support. */ childrenMayBecomeUndefined = true; dirtyChildren = getInvalidSizedChildren(true); } super.setHeight(height, unit); repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined, true); } /** * {@inheritDoc} * * @deprecated As of 7.0, use {@link #iterator()} instead. */ @Deprecated @Override public Iterator getComponentIterator() { return iterator(); } @Override protected AbstractComponentContainerState getState() { return (AbstractComponentContainerState) super.getState(); } @Override protected AbstractComponentContainerState getState(boolean markAsDirty) { return (AbstractComponentContainerState) super.getState(markAsDirty); } } / .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# Redmine - project management software
# Copyright (C) 2006-2015  Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

require File.expand_path('../../../../../test_helper', __FILE__)

class PdfTest < ActiveSupport::TestCase
  fixtures :users, :projects, :roles, :members, :member_roles,
           :enabled_modules, :issues, :trackers, :attachments

  def test_fix_text_encoding_nil
    assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "UTF-8")
    assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "ISO-8859-1")
  end

  def test_rdm_pdf_iconv_cannot_convert_ja_cp932
    utf8_txt_1  = "\xe7\x8b\x80\xe6\x85\x8b"
    utf8_txt_2  = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
    utf8_txt_3  = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
    ["CP932", "SJIS"].each do |encoding|
      txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
      txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
      txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
      assert_equal "?\x91\xd4".force_encoding("ASCII-8BIT"), txt_1
      assert_equal "?\x91\xd4?".force_encoding("ASCII-8BIT"), txt_2
      assert_equal "??\x91\xd4?".force_encoding("ASCII-8BIT"), txt_3
      assert_equal "ASCII-8BIT", txt_1.encoding.to_s
      assert_equal "ASCII-8BIT", txt_2.encoding.to_s
      assert_equal "ASCII-8BIT", txt_3.encoding.to_s
    end
  end

  def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
    str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
    str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
    txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
    txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
    assert_equal "ASCII-8BIT", txt_1.encoding.to_s
    assert_equal "ASCII-8BIT", txt_2.encoding.to_s
    assert_equal "Texte encod? en ISO-8859-1", txt_1
    assert_equal "?a?b?c?d?e test", txt_2
  end

  def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
    str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
    str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
    encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
    txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
    txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
    assert_equal "ASCII-8BIT", txt_1.encoding.to_s
    assert_equal "ASCII-8BIT", txt_2.encoding.to_s
    assert_equal "Texte encod? en ISO-8859-1", txt_1
    assert_equal "?a?b?c?d?e test", txt_2
  end

  def test_attach
    ["CP932", "SJIS"].each do |encoding|
      set_fixtures_attachments_directory

      str2 = "\x83e\x83X\x83g".force_encoding("ASCII-8BIT")

      a1 = Attachment.find(17)
      a2 = Attachment.find(19)
      User.current = User.find(1)
      assert a1.readable?
      assert a1.visible?
      assert a2.readable?
      assert a2.visible?

      aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
      assert_not_nil aa1
      assert_equal 17, aa1.id

      aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
      assert_not_nil aa2
      assert_equal 19, aa2.id

      User.current = nil
      assert a1.readable?
      assert (! a1.visible?)
      assert a2.readable?
      assert (! a2.visible?)
      aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
      assert_equal nil, aa1
      aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
      assert_equal nil, aa2

      set_tmp_attachments_directory
    end
  end
end